@livepeer-frameworks/streamcrafter-wc 0.1.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/cjs/components/fw-sc-advanced.js +198 -0
- package/dist/cjs/components/fw-sc-advanced.js.map +1 -0
- package/dist/cjs/components/fw-sc-compositor.js +116 -0
- package/dist/cjs/components/fw-sc-compositor.js.map +1 -0
- package/dist/cjs/components/fw-sc-layer-list.js +253 -0
- package/dist/cjs/components/fw-sc-layer-list.js.map +1 -0
- package/dist/cjs/components/fw-sc-scene-switcher.js +164 -0
- package/dist/cjs/components/fw-sc-scene-switcher.js.map +1 -0
- package/dist/cjs/components/fw-sc-volume.js +183 -0
- package/dist/cjs/components/fw-sc-volume.js.map +1 -0
- package/dist/cjs/components/fw-streamcrafter.js +508 -0
- package/dist/cjs/components/fw-streamcrafter.js.map +1 -0
- package/dist/cjs/controllers/ingest-controller-host.js +236 -0
- package/dist/cjs/controllers/ingest-controller-host.js.map +1 -0
- package/dist/cjs/define.js +25 -0
- package/dist/cjs/define.js.map +1 -0
- package/dist/cjs/icons/index.js +283 -0
- package/dist/cjs/icons/index.js.map +1 -0
- package/dist/cjs/index.js +38 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/node_modules/.pnpm/@rollup_plugin-typescript@12.3.0_rollup@4.57.1_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js +33 -0
- package/dist/cjs/node_modules/.pnpm/@rollup_plugin-typescript@12.3.0_rollup@4.57.1_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js.map +1 -0
- package/dist/cjs/styles/shared-styles.js +2019 -0
- package/dist/cjs/styles/shared-styles.js.map +1 -0
- package/dist/cjs/styles/utility-styles.js +182 -0
- package/dist/cjs/styles/utility-styles.js.map +1 -0
- package/dist/esm/components/fw-sc-advanced.js +198 -0
- package/dist/esm/components/fw-sc-advanced.js.map +1 -0
- package/dist/esm/components/fw-sc-compositor.js +116 -0
- package/dist/esm/components/fw-sc-compositor.js.map +1 -0
- package/dist/esm/components/fw-sc-layer-list.js +253 -0
- package/dist/esm/components/fw-sc-layer-list.js.map +1 -0
- package/dist/esm/components/fw-sc-scene-switcher.js +164 -0
- package/dist/esm/components/fw-sc-scene-switcher.js.map +1 -0
- package/dist/esm/components/fw-sc-volume.js +183 -0
- package/dist/esm/components/fw-sc-volume.js.map +1 -0
- package/dist/esm/components/fw-streamcrafter.js +508 -0
- package/dist/esm/components/fw-streamcrafter.js.map +1 -0
- package/dist/esm/controllers/ingest-controller-host.js +234 -0
- package/dist/esm/controllers/ingest-controller-host.js.map +1 -0
- package/dist/esm/define.js +23 -0
- package/dist/esm/define.js.map +1 -0
- package/dist/esm/icons/index.js +253 -0
- package/dist/esm/icons/index.js.map +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/node_modules/.pnpm/@rollup_plugin-typescript@12.3.0_rollup@4.57.1_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js +31 -0
- package/dist/esm/node_modules/.pnpm/@rollup_plugin-typescript@12.3.0_rollup@4.57.1_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js.map +1 -0
- package/dist/esm/styles/shared-styles.js +2017 -0
- package/dist/esm/styles/shared-styles.js.map +1 -0
- package/dist/esm/styles/utility-styles.js +180 -0
- package/dist/esm/styles/utility-styles.js.map +1 -0
- package/dist/fw-streamcrafter.iife.js +3121 -0
- package/dist/fw-streamcrafter.iife.js.map +1 -0
- package/dist/types/components/fw-sc-advanced.d.ts +20 -0
- package/dist/types/components/fw-sc-compositor.d.ts +19 -0
- package/dist/types/components/fw-sc-layer-list.d.ts +30 -0
- package/dist/types/components/fw-sc-scene-switcher.d.ts +23 -0
- package/dist/types/components/fw-sc-volume.d.ts +30 -0
- package/dist/types/components/fw-streamcrafter.d.ts +49 -0
- package/dist/types/controllers/ingest-controller-host.d.ts +77 -0
- package/dist/types/define.d.ts +1 -0
- package/dist/types/icons/index.d.ts +29 -0
- package/dist/types/iife-entry.d.ts +11 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/styles/shared-styles.d.ts +1 -0
- package/dist/types/styles/utility-styles.d.ts +1 -0
- package/package.json +55 -0
- package/src/components/fw-sc-advanced.ts +221 -0
- package/src/components/fw-sc-compositor.ts +162 -0
- package/src/components/fw-sc-layer-list.ts +251 -0
- package/src/components/fw-sc-scene-switcher.ts +163 -0
- package/src/components/fw-sc-volume.ts +171 -0
- package/src/components/fw-streamcrafter.ts +515 -0
- package/src/controllers/ingest-controller-host.ts +358 -0
- package/src/define.ts +23 -0
- package/src/icons/index.ts +291 -0
- package/src/iife-entry.ts +11 -0
- package/src/index.ts +15 -0
- package/src/styles/shared-styles.ts +2014 -0
- package/src/styles/utility-styles.ts +177 -0
|
@@ -0,0 +1,3121 @@
|
|
|
1
|
+
var FwStreamCrafter=function(e){"use strict";var t="undefined"!=typeof document?document.currentScript:null;function r(e,t,r,i){var s,o=arguments.length,n=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(s=e[a])&&(n=(o<3?s(n):o>3?s(t,r,n):s(t,r))||n);return o>3&&n&&Object.defineProperty(t,r,n),n}"function"==typeof SuppressedError&&SuppressedError;
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2019 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
const i=globalThis,s=i.ShadowRoot&&(void 0===i.ShadyCSS||i.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,o=Symbol(),n=new WeakMap;let a=class{constructor(e,t,r){if(this._$cssResult$=!0,r!==o)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const t=this.t;if(s&&void 0===e){const r=void 0!==t&&1===t.length;r&&(e=n.get(t)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),r&&n.set(t,e))}return e}toString(){return this.cssText}};const c=(e,...t)=>{const r=1===e.length?e[0]:t.reduce((t,r,i)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(r)+e[i+1],e[0]);return new a(r,e,o)},l=s?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const r of e.cssRules)t+=r.cssText;return(e=>new a("string"==typeof e?e:e+"",void 0,o))(t)})(e):e,{is:d,defineProperty:h,getOwnPropertyDescriptor:u,getOwnPropertyNames:p,getOwnPropertySymbols:g,getPrototypeOf:f}=Object,m=globalThis,w=m.trustedTypes,v=w?w.emptyScript:"",y=m.reactiveElementPolyfillSupport,b=(e,t)=>e,x={toAttribute(e,t){switch(t){case Boolean:e=e?v:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let r=e;switch(t){case Boolean:r=null!==e;break;case Number:r=null===e?null:Number(e);break;case Object:case Array:try{r=JSON.parse(e)}catch(e){r=null}}return r}},S=(e,t)=>!d(e,t),k={attribute:!0,type:String,converter:x,reflect:!1,useDefault:!1,hasChanged:S};
|
|
8
|
+
/**
|
|
9
|
+
* @license
|
|
10
|
+
* Copyright 2017 Google LLC
|
|
11
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
12
|
+
*/Symbol.metadata??=Symbol("metadata"),m.litPropertyMetadata??=new WeakMap;let C=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=k){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const r=Symbol(),i=this.getPropertyDescriptor(e,r,t);void 0!==i&&h(this.prototype,e,i)}}static getPropertyDescriptor(e,t,r){const{get:i,set:s}=u(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:i,set(t){const o=i?.call(this);s?.call(this,t),this.requestUpdate(e,o,r)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??k}static _$Ei(){if(this.hasOwnProperty(b("elementProperties")))return;const e=f(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(b("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(b("properties"))){const e=this.properties,t=[...p(e),...g(e)];for(const r of t)this.createProperty(r,e[r])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,r]of t)this.elementProperties.set(e,r)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const r=this._$Eu(e,t);void 0!==r&&this._$Eh.set(r,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const r=new Set(e.flat(1/0).reverse());for(const e of r)t.unshift(l(e))}else void 0!==e&&t.push(l(e));return t}static _$Eu(e,t){const r=t.attribute;return!1===r?void 0:"string"==typeof r?r:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const r of t.keys())this.hasOwnProperty(r)&&(e.set(r,this[r]),delete this[r]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const e=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((e,t)=>{if(s)e.adoptedStyleSheets=t.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const r of t){const t=document.createElement("style"),s=i.litNonce;void 0!==s&&t.setAttribute("nonce",s),t.textContent=r.cssText,e.appendChild(t)}})(e,this.constructor.elementStyles),e}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,r){this._$AK(e,r)}_$ET(e,t){const r=this.constructor.elementProperties.get(e),i=this.constructor._$Eu(e,r);if(void 0!==i&&!0===r.reflect){const s=(void 0!==r.converter?.toAttribute?r.converter:x).toAttribute(t,r.type);this._$Em=e,null==s?this.removeAttribute(i):this.setAttribute(i,s),this._$Em=null}}_$AK(e,t){const r=this.constructor,i=r._$Eh.get(e);if(void 0!==i&&this._$Em!==i){const e=r.getPropertyOptions(i),s="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:x;this._$Em=i;const o=s.fromAttribute(t,e.type);this[i]=o??this._$Ej?.get(i)??o,this._$Em=null}}requestUpdate(e,t,r,i=!1,s){if(void 0!==e){const o=this.constructor;if(!1===i&&(s=this[e]),r??=o.getPropertyOptions(e),!((r.hasChanged??S)(s,t)||r.useDefault&&r.reflect&&s===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,r))))return;this.C(e,t,r)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:r,reflect:i,wrapped:s},o){r&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==s||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||r||(t=void 0),this._$AL.set(e,t)),!0===i&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,r]of e){const{wrapped:e}=r,i=this[t];!0!==e||this._$AL.has(t)||void 0===i||this.C(t,void 0,r,i)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};C.elementStyles=[],C.shadowRootOptions={mode:"open"},C[b("elementProperties")]=new Map,C[b("finalized")]=new Map,y?.({ReactiveElement:C}),(m.reactiveElementVersions??=[]).push("2.1.2");
|
|
13
|
+
/**
|
|
14
|
+
* @license
|
|
15
|
+
* Copyright 2017 Google LLC
|
|
16
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
17
|
+
*/
|
|
18
|
+
const $=globalThis,M=e=>e,T=$.trustedTypes,A=T?T.createPolicy("lit-html",{createHTML:e=>e}):void 0,_="$lit$",E=`lit$${Math.random().toFixed(9).slice(2)}$`,I="?"+E,P=`<${I}>`,R=document,W=()=>R.createComment(""),z=e=>null===e||"object"!=typeof e&&"function"!=typeof e,U=Array.isArray,L="[ \t\n\f\r]",D=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,V=/-->/g,O=/>/g,j=RegExp(`>|${L}(?:([^\\s"'>=/]+)(${L}*=${L}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),N=/'/g,F=/"/g,H=/^(?:script|style|textarea|title)$/i,q=(e=>(t,...r)=>({_$litType$:e,strings:t,values:r}))(1),B=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),Q=new WeakMap,X=R.createTreeWalker(R,129);function K(e,t){if(!U(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==A?A.createHTML(t):t}const J=(e,t)=>{const r=e.length-1,i=[];let s,o=2===t?"<svg>":3===t?"<math>":"",n=D;for(let t=0;t<r;t++){const r=e[t];let a,c,l=-1,d=0;for(;d<r.length&&(n.lastIndex=d,c=n.exec(r),null!==c);)d=n.lastIndex,n===D?"!--"===c[1]?n=V:void 0!==c[1]?n=O:void 0!==c[2]?(H.test(c[2])&&(s=RegExp("</"+c[2],"g")),n=j):void 0!==c[3]&&(n=j):n===j?">"===c[0]?(n=s??D,l=-1):void 0===c[1]?l=-2:(l=n.lastIndex-c[2].length,a=c[1],n=void 0===c[3]?j:'"'===c[3]?F:N):n===F||n===N?n=j:n===V||n===O?n=D:(n=j,s=void 0);const h=n===j&&e[t+1].startsWith("/>")?" ":"";o+=n===D?r+P:l>=0?(i.push(a),r.slice(0,l)+_+r.slice(l)+E+h):r+E+(-2===l?t:h)}return[K(e,o+(e[r]||"<?>")+(2===t?"</svg>":3===t?"</math>":"")),i]};class Z{constructor({strings:e,_$litType$:t},r){let i;this.parts=[];let s=0,o=0;const n=e.length-1,a=this.parts,[c,l]=J(e,t);if(this.el=Z.createElement(c,r),X.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(i=X.nextNode())&&a.length<n;){if(1===i.nodeType){if(i.hasAttributes())for(const e of i.getAttributeNames())if(e.endsWith(_)){const t=l[o++],r=i.getAttribute(e).split(E),n=/([.?@])?(.*)/.exec(t);a.push({type:1,index:s,name:n[2],strings:r,ctor:"."===n[1]?ie:"?"===n[1]?se:"@"===n[1]?oe:re}),i.removeAttribute(e)}else e.startsWith(E)&&(a.push({type:6,index:s}),i.removeAttribute(e));if(H.test(i.tagName)){const e=i.textContent.split(E),t=e.length-1;if(t>0){i.textContent=T?T.emptyScript:"";for(let r=0;r<t;r++)i.append(e[r],W()),X.nextNode(),a.push({type:2,index:++s});i.append(e[t],W())}}}else if(8===i.nodeType)if(i.data===I)a.push({type:2,index:s});else{let e=-1;for(;-1!==(e=i.data.indexOf(E,e+1));)a.push({type:7,index:s}),e+=E.length-1}s++}}static createElement(e,t){const r=R.createElement("template");return r.innerHTML=e,r}}function Y(e,t,r=e,i){if(t===B)return t;let s=void 0!==i?r._$Co?.[i]:r._$Cl;const o=z(t)?void 0:t._$litDirective$;return s?.constructor!==o&&(s?._$AO?.(!1),void 0===o?s=void 0:(s=new o(e),s._$AT(e,r,i)),void 0!==i?(r._$Co??=[])[i]=s:r._$Cl=s),void 0!==s&&(t=Y(e,s._$AS(e,t.values),s,i)),t}class ee{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){const{el:{content:t},parts:r}=this._$AD,i=(e?.creationScope??R).importNode(t,!0);X.currentNode=i;let s=X.nextNode(),o=0,n=0,a=r[0];for(;void 0!==a;){if(o===a.index){let t;2===a.type?t=new te(s,s.nextSibling,this,e):1===a.type?t=new a.ctor(s,a.name,a.strings,this,e):6===a.type&&(t=new ne(s,this,e)),this._$AV.push(t),a=r[++n]}o!==a?.index&&(s=X.nextNode(),o++)}return X.currentNode=R,i}p(e){let t=0;for(const r of this._$AV)void 0!==r&&(void 0!==r.strings?(r._$AI(e,r,t),t+=r.strings.length-2):r._$AI(e[t])),t++}}class te{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,r,i){this.type=2,this._$AH=G,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=r,this.options=i,this._$Cv=i?.isConnected??!0}get parentNode(){let e=this._$AA.parentNode;const t=this._$AM;return void 0!==t&&11===e?.nodeType&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=Y(this,e,t),z(e)?e===G||null==e||""===e?(this._$AH!==G&&this._$AR(),this._$AH=G):e!==this._$AH&&e!==B&&this._(e):void 0!==e._$litType$?this.$(e):void 0!==e.nodeType?this.T(e):(e=>U(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==G&&z(this._$AH)?this._$AA.nextSibling.data=e:this.T(R.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:r}=e,i="number"==typeof r?this._$AC(e):(void 0===r.el&&(r.el=Z.createElement(K(r.h,r.h[0]),this.options)),r);if(this._$AH?._$AD===i)this._$AH.p(t);else{const e=new ee(i,this),r=e.u(this.options);e.p(t),this.T(r),this._$AH=e}}_$AC(e){let t=Q.get(e.strings);return void 0===t&&Q.set(e.strings,t=new Z(e)),t}k(e){U(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let r,i=0;for(const s of e)i===t.length?t.push(r=new te(this.O(W()),this.O(W()),this,this.options)):r=t[i],r._$AI(s),i++;i<t.length&&(this._$AR(r&&r._$AB.nextSibling,i),t.length=i)}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(!1,!0,t);e!==this._$AB;){const t=M(e).nextSibling;M(e).remove(),e=t}}setConnected(e){void 0===this._$AM&&(this._$Cv=e,this._$AP?.(e))}}class re{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,r,i,s){this.type=1,this._$AH=G,this._$AN=void 0,this.element=e,this.name=t,this._$AM=i,this.options=s,r.length>2||""!==r[0]||""!==r[1]?(this._$AH=Array(r.length-1).fill(new String),this.strings=r):this._$AH=G}_$AI(e,t=this,r,i){const s=this.strings;let o=!1;if(void 0===s)e=Y(this,e,t,0),o=!z(e)||e!==this._$AH&&e!==B,o&&(this._$AH=e);else{const i=e;let n,a;for(e=s[0],n=0;n<s.length-1;n++)a=Y(this,i[r+n],t,n),a===B&&(a=this._$AH[n]),o||=!z(a)||a!==this._$AH[n],a===G?e=G:e!==G&&(e+=(a??"")+s[n+1]),this._$AH[n]=a}o&&!i&&this.j(e)}j(e){e===G?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,e??"")}}class ie extends re{constructor(){super(...arguments),this.type=3}j(e){this.element[this.name]=e===G?void 0:e}}class se extends re{constructor(){super(...arguments),this.type=4}j(e){this.element.toggleAttribute(this.name,!!e&&e!==G)}}class oe extends re{constructor(e,t,r,i,s){super(e,t,r,i,s),this.type=5}_$AI(e,t=this){if((e=Y(this,e,t,0)??G)===B)return;const r=this._$AH,i=e===G&&r!==G||e.capture!==r.capture||e.once!==r.once||e.passive!==r.passive,s=e!==G&&(r===G||i);i&&this.element.removeEventListener(this.name,this,r),s&&this.element.addEventListener(this.name,this,e),this._$AH=e}handleEvent(e){"function"==typeof this._$AH?this._$AH.call(this.options?.host??this.element,e):this._$AH.handleEvent(e)}}class ne{constructor(e,t,r){this.element=e,this.type=6,this._$AN=void 0,this._$AM=t,this.options=r}get _$AU(){return this._$AM._$AU}_$AI(e){Y(this,e)}}const ae=$.litHtmlPolyfillSupport;ae?.(Z,te),($.litHtmlVersions??=[]).push("3.3.2");const ce=globalThis;
|
|
19
|
+
/**
|
|
20
|
+
* @license
|
|
21
|
+
* Copyright 2017 Google LLC
|
|
22
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
23
|
+
*/let le=class extends C{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=((e,t,r)=>{const i=r?.renderBefore??t;let s=i._$litPart$;if(void 0===s){const e=r?.renderBefore??null;i._$litPart$=s=new te(t.insertBefore(W(),e),e,void 0,r??{})}return s._$AI(e),s})(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return B}};le._$litElement$=!0,le.finalized=!0,ce.litElementHydrateSupport?.({LitElement:le});const de=ce.litElementPolyfillSupport;de?.({LitElement:le}),(ce.litElementVersions??=[]).push("4.2.2");
|
|
24
|
+
/**
|
|
25
|
+
* @license
|
|
26
|
+
* Copyright 2017 Google LLC
|
|
27
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
28
|
+
*/
|
|
29
|
+
const he=e=>(t,r)=>{void 0!==r?r.addInitializer(()=>{customElements.define(e,t)}):customElements.define(e,t)},ue={attribute:!0,type:String,converter:x,reflect:!1,hasChanged:S},pe=(e=ue,t,r)=>{const{kind:i,metadata:s}=r;let o=globalThis.litPropertyMetadata.get(s);if(void 0===o&&globalThis.litPropertyMetadata.set(s,o=new Map),"setter"===i&&((e=Object.create(e)).wrapped=!0),o.set(r.name,e),"accessor"===i){const{name:i}=r;return{set(r){const s=t.get.call(this);t.set.call(this,r),this.requestUpdate(i,s,e,!0,r)},init(t){return void 0!==t&&this.C(i,void 0,e,t),t}}}if("setter"===i){const{name:i}=r;return function(r){const s=this[i];t.call(this,r),this.requestUpdate(i,s,e,!0,r)}}throw Error("Unsupported decorator location: "+i)};
|
|
30
|
+
/**
|
|
31
|
+
* @license
|
|
32
|
+
* Copyright 2017 Google LLC
|
|
33
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
34
|
+
*/function ge(e){return(t,r)=>"object"==typeof r?pe(e,t,r):((e,t,r)=>{const i=t.hasOwnProperty(r);return t.constructor.createProperty(r,e),i?Object.getOwnPropertyDescriptor(t,r):void 0})(e,t,r)}
|
|
35
|
+
/**
|
|
36
|
+
* @license
|
|
37
|
+
* Copyright 2017 Google LLC
|
|
38
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
39
|
+
*/function fe(e){return ge({...e,state:!0,attribute:!1})}
|
|
40
|
+
/**
|
|
41
|
+
* @license
|
|
42
|
+
* Copyright 2017 Google LLC
|
|
43
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* @license
|
|
47
|
+
* Copyright 2017 Google LLC
|
|
48
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
49
|
+
*/
|
|
50
|
+
function me(e,t){return(t,r,i)=>((e,t,r)=>(r.configurable=!0,r.enumerable=!0,Reflect.decorate&&"object"!=typeof t&&Object.defineProperty(e,t,r),r))(t,r,{get(){return(t=>t.renderRoot?.querySelector(e)??null)(this)}})}
|
|
51
|
+
/**
|
|
52
|
+
* @license
|
|
53
|
+
* Copyright 2017 Google LLC
|
|
54
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
55
|
+
*/const we=1;class ve{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,r){this._$Ct=e,this._$AM=t,this._$Ci=r}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}}
|
|
56
|
+
/**
|
|
57
|
+
* @license
|
|
58
|
+
* Copyright 2018 Google LLC
|
|
59
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
60
|
+
*/const ye=(e=>(...t)=>({_$litDirective$:e,values:t}))(class extends ve{constructor(e){if(super(e),e.type!==we||"class"!==e.name||e.strings?.length>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(e){return" "+Object.keys(e).filter(t=>e[t]).join(" ")+" "}update(e,[t]){if(void 0===this.st){this.st=new Set,void 0!==e.strings&&(this.nt=new Set(e.strings.join(" ").split(/\s/).filter(e=>""!==e)));for(const e in t)t[e]&&!this.nt?.has(e)&&this.st.add(e);return this.render(t)}const r=e.element.classList;for(const e of this.st)e in t||(r.remove(e),this.st.delete(e));for(const e in t){const i=!!t[e];i===this.st.has(e)||this.nt?.has(e)||(i?(r.add(e),this.st.add(e)):(r.remove(e),this.st.delete(e)))}return B}}),be=c`
|
|
61
|
+
/**
|
|
62
|
+
* StreamCrafter CSS
|
|
63
|
+
* Wrapped in @layer fw-streamcrafter for cascade isolation.
|
|
64
|
+
* Host app unlayered styles will always take precedence.
|
|
65
|
+
*
|
|
66
|
+
* Import this file in your application:
|
|
67
|
+
* import '@livepeer-frameworks/streamcrafter-core/styles/streamcrafter.css';
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
/* Declare layer upfront for lowest priority */
|
|
71
|
+
@layer fw-streamcrafter;
|
|
72
|
+
|
|
73
|
+
@layer fw-streamcrafter {
|
|
74
|
+
/* =============================================
|
|
75
|
+
Root Container (with scoped CSS variables)
|
|
76
|
+
============================================= */
|
|
77
|
+
.fw-sc-root {
|
|
78
|
+
/* Tokyo Night color tokens - scoped to component */
|
|
79
|
+
--tn-bg-dark: 240 15% 10%; /* #16161e - Darkest surface */
|
|
80
|
+
--tn-bg: 235 19% 13%; /* #1a1b26 - Main background */
|
|
81
|
+
--tn-bg-highlight: 229 24% 19%; /* #24283b - Elevated surfaces */
|
|
82
|
+
|
|
83
|
+
--tn-fg: 229 73% 86%; /* #c0caf5 - Primary text */
|
|
84
|
+
--tn-fg-dark: 229 35% 75%; /* #a9b1d6 - Secondary text */
|
|
85
|
+
--tn-fg-gutter: 229 24% 31%; /* #3b4261 - Borders, seams */
|
|
86
|
+
--tn-comment: 229 23% 44%; /* #565f89 - Comments, muted */
|
|
87
|
+
|
|
88
|
+
--tn-blue: 225 86% 70%; /* #7aa2f7 - Primary actions */
|
|
89
|
+
--tn-green: 89 51% 61%; /* #9ece6a - Success, live */
|
|
90
|
+
--tn-red: 349 89% 72%; /* #f7768e - Destructive, errors */
|
|
91
|
+
--tn-yellow: 36 66% 64%; /* #e0af68 - Warnings */
|
|
92
|
+
--tn-purple: 264 85% 74%; /* #bb9af7 - Special */
|
|
93
|
+
--tn-cyan: 197 95% 74%; /* #7dcfff - Info */
|
|
94
|
+
|
|
95
|
+
/* Component styles */
|
|
96
|
+
background: hsl(var(--tn-bg-dark));
|
|
97
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
98
|
+
font-family:
|
|
99
|
+
system-ui,
|
|
100
|
+
-apple-system,
|
|
101
|
+
BlinkMacSystemFont,
|
|
102
|
+
"Segoe UI",
|
|
103
|
+
Roboto,
|
|
104
|
+
sans-serif;
|
|
105
|
+
font-size: 14px;
|
|
106
|
+
line-height: 1.5;
|
|
107
|
+
color: hsl(var(--tn-fg));
|
|
108
|
+
/* Allow root to take full height of parent and manage its children */
|
|
109
|
+
height: 100%;
|
|
110
|
+
min-height: 500px;
|
|
111
|
+
display: flex;
|
|
112
|
+
flex-direction: column;
|
|
113
|
+
|
|
114
|
+
overflow: hidden; /* Prevent content from overflowing the root container */
|
|
115
|
+
|
|
116
|
+
/* Container query support for responsive layout */
|
|
117
|
+
container-type: inline-size;
|
|
118
|
+
container-name: streamcrafter;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Dev mode - flex layout for side panel */
|
|
122
|
+
.fw-sc-root--devmode {
|
|
123
|
+
display: flex;
|
|
124
|
+
flex-direction: row;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* Main content wrapper */
|
|
128
|
+
.fw-sc-main {
|
|
129
|
+
display: flex;
|
|
130
|
+
flex-direction: column;
|
|
131
|
+
min-width: 0;
|
|
132
|
+
/* Occupy remaining vertical space in root */
|
|
133
|
+
flex: 1;
|
|
134
|
+
overflow: hidden; /* Manage internal overflow */
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* Content area (preview + mixer) - responsive layout */
|
|
138
|
+
.fw-sc-content {
|
|
139
|
+
display: flex;
|
|
140
|
+
flex-direction: column;
|
|
141
|
+
flex: 1; /* Occupy remaining vertical space in fw-sc-main */
|
|
142
|
+
min-height: 0; /* Allow flex item to shrink below content size */
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Preview wrapper for flex sizing */
|
|
146
|
+
.fw-sc-preview-wrapper {
|
|
147
|
+
display: flex;
|
|
148
|
+
flex-direction: column;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* Mixer panel class for responsive targeting */
|
|
152
|
+
.fw-sc-mixer {
|
|
153
|
+
/* Default: takes full width below preview */
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* =============================================
|
|
157
|
+
Responsive Layout: Mixer on Right (Wide Screens)
|
|
158
|
+
Uses container queries for container-aware layout
|
|
159
|
+
============================================= */
|
|
160
|
+
@container streamcrafter (min-width: 600px) {
|
|
161
|
+
.fw-sc-content {
|
|
162
|
+
flex-direction: row;
|
|
163
|
+
align-items: stretch; /* Ensure full height */
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.fw-sc-preview-wrapper {
|
|
167
|
+
flex: 1;
|
|
168
|
+
min-width: 0;
|
|
169
|
+
/* Center preview vertically if needed */
|
|
170
|
+
display: flex;
|
|
171
|
+
flex-direction: column;
|
|
172
|
+
justify-content: center;
|
|
173
|
+
background: black;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.fw-sc-mixer {
|
|
177
|
+
width: 320px; /* Slightly wider for better ergonomics */
|
|
178
|
+
flex-shrink: 0;
|
|
179
|
+
border-left: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
180
|
+
border-bottom: none;
|
|
181
|
+
background: hsl(var(--tn-bg));
|
|
182
|
+
display: flex;
|
|
183
|
+
flex-direction: column;
|
|
184
|
+
max-height: none;
|
|
185
|
+
overflow-y: auto;
|
|
186
|
+
transition: width 0.2s ease-out; /* Smooth collapse transition */
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* Collapsed state for sidebar mixer */
|
|
190
|
+
.fw-sc-mixer.fw-sc-section--collapsed {
|
|
191
|
+
width: 48px; /* Collapsed width */
|
|
192
|
+
overflow-x: hidden; /* Hide overflowing content */
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.fw-sc-mixer.fw-sc-section--collapsed .fw-sc-section-header {
|
|
196
|
+
justify-content: center; /* Center icon */
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.fw-sc-mixer.fw-sc-section--collapsed .fw-sc-section-header span {
|
|
200
|
+
display: none; /* Hide text */
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.fw-sc-mixer.fw-sc-section--collapsed .fw-sc-section-header svg {
|
|
204
|
+
transform: rotate(0deg) !important; /* Reset chevron rotation */
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.fw-sc-mixer.fw-sc-section--collapsed .fw-sc-sources {
|
|
208
|
+
display: none; /* Hide source list content */
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* Redesign Source Items for Sidebar Context */
|
|
212
|
+
.fw-sc-mixer .fw-sc-source {
|
|
213
|
+
display: flex;
|
|
214
|
+
align-items: center;
|
|
215
|
+
gap: 0.5rem;
|
|
216
|
+
padding: 0.375rem 0.5rem;
|
|
217
|
+
background: hsl(var(--tn-bg-highlight) / 0.1);
|
|
218
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
219
|
+
transition: background 0.2s;
|
|
220
|
+
height: 48px;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.fw-sc-mixer .fw-sc-source:hover {
|
|
224
|
+
background: hsl(var(--tn-bg-highlight) / 0.3);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* Icon */
|
|
228
|
+
.fw-sc-mixer .fw-sc-source-icon {
|
|
229
|
+
flex: 0 0 auto;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Name + Type */
|
|
233
|
+
.fw-sc-mixer .fw-sc-source-info {
|
|
234
|
+
flex: 1;
|
|
235
|
+
min-width: 0; /* Enable truncation */
|
|
236
|
+
display: flex;
|
|
237
|
+
flex-direction: column;
|
|
238
|
+
gap: 0;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.fw-sc-mixer .fw-sc-source-label {
|
|
242
|
+
font-size: 0.8rem;
|
|
243
|
+
font-weight: 600;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Hide the source type in sidebar to save space */
|
|
247
|
+
.fw-sc-mixer .fw-sc-source-type {
|
|
248
|
+
display: none;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* Controls */
|
|
252
|
+
.fw-sc-mixer .fw-sc-source-controls {
|
|
253
|
+
display: flex;
|
|
254
|
+
align-items: center;
|
|
255
|
+
gap: 0.25rem;
|
|
256
|
+
margin: 0;
|
|
257
|
+
width: auto;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* Volume Slider fixed width in compact mode */
|
|
261
|
+
.fw-sc-mixer .fw-sc-volume-slider {
|
|
262
|
+
width: 60px;
|
|
263
|
+
flex: 0 0 auto;
|
|
264
|
+
height: 6px;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* Hide the percentage text in this compact view */
|
|
268
|
+
.fw-sc-mixer .fw-sc-volume-label {
|
|
269
|
+
display: none;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* Advanced Panel styling - matches Player DevModePanel */
|
|
274
|
+
.fw-sc-advanced-panel {
|
|
275
|
+
background: hsl(var(--tn-bg));
|
|
276
|
+
border-left: 1px solid hsl(var(--tn-fg-gutter) / 0.5);
|
|
277
|
+
width: 280px;
|
|
278
|
+
flex-shrink: 0;
|
|
279
|
+
display: flex;
|
|
280
|
+
flex-direction: column;
|
|
281
|
+
height: 100%;
|
|
282
|
+
overflow: hidden;
|
|
283
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
284
|
+
font-size: 12px;
|
|
285
|
+
z-index: 40;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* =============================================
|
|
289
|
+
Header Zone
|
|
290
|
+
============================================= */
|
|
291
|
+
.fw-sc-header {
|
|
292
|
+
display: flex;
|
|
293
|
+
align-items: center;
|
|
294
|
+
justify-content: space-between;
|
|
295
|
+
padding: 0.5rem 1rem;
|
|
296
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
297
|
+
background: hsl(var(--tn-bg-highlight) / 0.5);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.fw-sc-header-title {
|
|
301
|
+
font-weight: 600;
|
|
302
|
+
text-transform: uppercase;
|
|
303
|
+
letter-spacing: 0.05em;
|
|
304
|
+
font-size: 0.75rem;
|
|
305
|
+
color: hsl(var(--tn-fg-dark));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.fw-sc-header-status {
|
|
309
|
+
display: flex;
|
|
310
|
+
align-items: center;
|
|
311
|
+
gap: 0.5rem;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.fw-sc-header-actions {
|
|
315
|
+
display: flex;
|
|
316
|
+
align-items: center;
|
|
317
|
+
gap: 0.25rem;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/* Header Button - visible gear/wrench like Player */
|
|
321
|
+
.fw-sc-header-btn {
|
|
322
|
+
display: flex;
|
|
323
|
+
align-items: center;
|
|
324
|
+
justify-content: center;
|
|
325
|
+
width: 28px;
|
|
326
|
+
height: 28px;
|
|
327
|
+
padding: 0;
|
|
328
|
+
border: none;
|
|
329
|
+
border-radius: 4px;
|
|
330
|
+
background: transparent;
|
|
331
|
+
color: hsl(var(--tn-comment));
|
|
332
|
+
cursor: pointer;
|
|
333
|
+
transition: all 0.15s ease;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.fw-sc-header-btn:hover {
|
|
337
|
+
background: hsl(var(--tn-bg-highlight));
|
|
338
|
+
color: hsl(var(--tn-fg));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.fw-sc-header-btn--active {
|
|
342
|
+
background: hsl(var(--tn-blue) / 0.2);
|
|
343
|
+
color: hsl(var(--tn-blue));
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.fw-sc-header-btn--active:hover {
|
|
347
|
+
background: hsl(var(--tn-blue) / 0.3);
|
|
348
|
+
color: hsl(var(--tn-blue));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/* =============================================
|
|
352
|
+
Video Preview (Flush - No Padding)
|
|
353
|
+
============================================= */
|
|
354
|
+
.fw-sc-preview {
|
|
355
|
+
position: relative;
|
|
356
|
+
/* Use flex-grow instead of aspect-ratio to fill available height */
|
|
357
|
+
flex: 1;
|
|
358
|
+
background: black;
|
|
359
|
+
overflow: hidden;
|
|
360
|
+
display: flex; /* Make it a flex container for the video */
|
|
361
|
+
align-items: center;
|
|
362
|
+
justify-content: center;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.fw-sc-preview video {
|
|
366
|
+
width: 100%;
|
|
367
|
+
height: 100%;
|
|
368
|
+
object-fit: contain; /* Ensure video fits within the preview area */
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.fw-sc-preview-placeholder {
|
|
372
|
+
position: absolute;
|
|
373
|
+
inset: 0;
|
|
374
|
+
display: flex;
|
|
375
|
+
flex-direction: column;
|
|
376
|
+
align-items: center;
|
|
377
|
+
justify-content: center;
|
|
378
|
+
color: hsl(var(--tn-comment));
|
|
379
|
+
gap: 0.5rem;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.fw-sc-preview-placeholder svg {
|
|
383
|
+
width: 48px;
|
|
384
|
+
height: 48px;
|
|
385
|
+
opacity: 0.5;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/* =============================================
|
|
389
|
+
Live Badge Overlay
|
|
390
|
+
============================================= */
|
|
391
|
+
.fw-sc-live-badge {
|
|
392
|
+
position: absolute;
|
|
393
|
+
top: 1rem;
|
|
394
|
+
right: 1rem;
|
|
395
|
+
display: flex;
|
|
396
|
+
align-items: center;
|
|
397
|
+
gap: 0.375rem;
|
|
398
|
+
padding: 0.25rem 0.5rem;
|
|
399
|
+
background: hsl(var(--tn-red));
|
|
400
|
+
color: white;
|
|
401
|
+
font-size: 0.7rem;
|
|
402
|
+
font-weight: 700;
|
|
403
|
+
text-transform: uppercase;
|
|
404
|
+
letter-spacing: 0.05em;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.fw-sc-live-badge::before {
|
|
408
|
+
content: "";
|
|
409
|
+
width: 0.5rem;
|
|
410
|
+
height: 0.5rem;
|
|
411
|
+
background: white;
|
|
412
|
+
border-radius: 50%;
|
|
413
|
+
animation: fw-sc-pulse 1.5s infinite;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
@keyframes fw-sc-pulse {
|
|
417
|
+
0%,
|
|
418
|
+
100% {
|
|
419
|
+
opacity: 1;
|
|
420
|
+
}
|
|
421
|
+
50% {
|
|
422
|
+
opacity: 0.5;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/* =============================================
|
|
427
|
+
Status Overlay
|
|
428
|
+
============================================= */
|
|
429
|
+
.fw-sc-status-overlay {
|
|
430
|
+
position: absolute;
|
|
431
|
+
inset: 0;
|
|
432
|
+
display: flex;
|
|
433
|
+
flex-direction: column;
|
|
434
|
+
align-items: center;
|
|
435
|
+
justify-content: center;
|
|
436
|
+
background: hsl(var(--tn-bg-dark) / 0.9);
|
|
437
|
+
gap: 1rem;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.fw-sc-status-spinner {
|
|
441
|
+
width: 32px;
|
|
442
|
+
height: 32px;
|
|
443
|
+
border: 3px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
444
|
+
border-top-color: hsl(var(--tn-blue));
|
|
445
|
+
border-radius: 50%;
|
|
446
|
+
animation: fw-sc-spin 1s linear infinite;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
@keyframes fw-sc-spin {
|
|
450
|
+
to {
|
|
451
|
+
transform: rotate(360deg);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.fw-sc-status-text {
|
|
456
|
+
font-size: 0.875rem;
|
|
457
|
+
color: hsl(var(--tn-fg-dark));
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/* =============================================
|
|
461
|
+
VU Meter
|
|
462
|
+
============================================= */
|
|
463
|
+
.fw-sc-vu-meter {
|
|
464
|
+
height: 8px;
|
|
465
|
+
background: hsl(var(--tn-bg-highlight));
|
|
466
|
+
position: relative;
|
|
467
|
+
overflow: hidden;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.fw-sc-vu-meter-fill {
|
|
471
|
+
height: 100%;
|
|
472
|
+
background: linear-gradient(
|
|
473
|
+
to right,
|
|
474
|
+
hsl(var(--tn-green)) 0%,
|
|
475
|
+
hsl(var(--tn-green)) 60%,
|
|
476
|
+
hsl(var(--tn-yellow)) 80%,
|
|
477
|
+
hsl(var(--tn-red)) 100%
|
|
478
|
+
);
|
|
479
|
+
transition: width 50ms ease-out;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.fw-sc-vu-meter-peak {
|
|
483
|
+
position: absolute;
|
|
484
|
+
top: 0;
|
|
485
|
+
height: 100%;
|
|
486
|
+
width: 2px;
|
|
487
|
+
background: hsl(var(--tn-fg));
|
|
488
|
+
transition: left 50ms ease-out;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/* Vertical VU Meter variant */
|
|
492
|
+
.fw-sc-vu-meter--vertical {
|
|
493
|
+
width: 4px;
|
|
494
|
+
height: 100%;
|
|
495
|
+
background: hsl(var(--tn-bg-highlight));
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.fw-sc-vu-meter--vertical .fw-sc-vu-meter-fill {
|
|
499
|
+
width: 100%;
|
|
500
|
+
background: linear-gradient(
|
|
501
|
+
to top,
|
|
502
|
+
hsl(var(--tn-green)) 0%,
|
|
503
|
+
hsl(var(--tn-green)) 60%,
|
|
504
|
+
hsl(var(--tn-yellow)) 80%,
|
|
505
|
+
hsl(var(--tn-red)) 100%
|
|
506
|
+
);
|
|
507
|
+
transition: height 50ms ease-out;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/* =============================================
|
|
511
|
+
Section (Collapsible Areas)
|
|
512
|
+
============================================= */
|
|
513
|
+
.fw-sc-section {
|
|
514
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.fw-sc-section:last-child {
|
|
518
|
+
border-bottom: none;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.fw-sc-section-header {
|
|
522
|
+
display: flex;
|
|
523
|
+
align-items: center;
|
|
524
|
+
justify-content: space-between;
|
|
525
|
+
padding: 0.5rem 1rem;
|
|
526
|
+
font-size: 0.7rem;
|
|
527
|
+
font-weight: 600;
|
|
528
|
+
text-transform: uppercase;
|
|
529
|
+
letter-spacing: 0.05em;
|
|
530
|
+
color: hsl(var(--tn-comment));
|
|
531
|
+
cursor: pointer;
|
|
532
|
+
user-select: none;
|
|
533
|
+
background: hsl(var(--tn-bg-highlight) / 0.3);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.fw-sc-section-header:hover {
|
|
537
|
+
background: hsl(var(--tn-bg-highlight) / 0.5);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.fw-sc-section-header svg {
|
|
541
|
+
width: 14px;
|
|
542
|
+
height: 14px;
|
|
543
|
+
transition: transform 0.2s;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.fw-sc-section--collapsed .fw-sc-section-header svg {
|
|
547
|
+
transform: rotate(-90deg);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.fw-sc-section-body {
|
|
551
|
+
padding: 0.75rem 1rem;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.fw-sc-section-body--flush {
|
|
555
|
+
padding: 0;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/* =============================================
|
|
559
|
+
Source List
|
|
560
|
+
============================================= */
|
|
561
|
+
.fw-sc-sources {
|
|
562
|
+
display: flex;
|
|
563
|
+
flex-direction: column;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.fw-sc-source {
|
|
567
|
+
display: flex;
|
|
568
|
+
align-items: center;
|
|
569
|
+
padding: 0.5rem 1rem;
|
|
570
|
+
gap: 0.5rem;
|
|
571
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.fw-sc-source:last-child {
|
|
575
|
+
border-bottom: none;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.fw-sc-source--hidden {
|
|
579
|
+
opacity: 0.5;
|
|
580
|
+
background: hsl(var(--tn-bg-dark) / 0.3);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.fw-sc-source-icon {
|
|
584
|
+
width: 24px;
|
|
585
|
+
height: 24px;
|
|
586
|
+
display: flex;
|
|
587
|
+
align-items: center;
|
|
588
|
+
justify-content: center;
|
|
589
|
+
color: hsl(var(--tn-comment));
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.fw-sc-source-info {
|
|
593
|
+
flex: 1;
|
|
594
|
+
min-width: 0;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.fw-sc-source-label {
|
|
598
|
+
font-size: 0.875rem;
|
|
599
|
+
font-weight: 500;
|
|
600
|
+
overflow: hidden;
|
|
601
|
+
text-overflow: ellipsis;
|
|
602
|
+
white-space: nowrap;
|
|
603
|
+
display: flex;
|
|
604
|
+
align-items: center;
|
|
605
|
+
gap: 0.5rem;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.fw-sc-primary-badge {
|
|
609
|
+
font-size: 0.55rem;
|
|
610
|
+
font-weight: 700;
|
|
611
|
+
text-transform: uppercase;
|
|
612
|
+
letter-spacing: 0.05em;
|
|
613
|
+
padding: 0.125rem 0.375rem;
|
|
614
|
+
background: hsl(var(--tn-green) / 0.2);
|
|
615
|
+
color: hsl(var(--tn-green));
|
|
616
|
+
border-radius: 2px;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.fw-sc-source-type {
|
|
620
|
+
font-size: 0.7rem;
|
|
621
|
+
color: hsl(var(--tn-comment));
|
|
622
|
+
text-transform: uppercase;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.fw-sc-source-controls {
|
|
626
|
+
display: flex;
|
|
627
|
+
align-items: center;
|
|
628
|
+
gap: 0.25rem;
|
|
629
|
+
flex-wrap: wrap; /* Allow items to wrap */
|
|
630
|
+
justify-content: flex-end; /* Align to the right when wrapped */
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/* =============================================
|
|
634
|
+
Icon Buttons (Small Controls)
|
|
635
|
+
============================================= */
|
|
636
|
+
.fw-sc-icon-btn {
|
|
637
|
+
width: 28px;
|
|
638
|
+
height: 28px;
|
|
639
|
+
display: flex;
|
|
640
|
+
align-items: center;
|
|
641
|
+
justify-content: center;
|
|
642
|
+
padding: 0;
|
|
643
|
+
border: none;
|
|
644
|
+
border-radius: 4px;
|
|
645
|
+
background: transparent;
|
|
646
|
+
color: hsl(var(--tn-comment));
|
|
647
|
+
cursor: pointer;
|
|
648
|
+
transition:
|
|
649
|
+
background 0.15s,
|
|
650
|
+
color 0.15s;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.fw-sc-icon-btn:hover {
|
|
654
|
+
background: hsl(var(--tn-bg-highlight) / 0.5);
|
|
655
|
+
color: hsl(var(--tn-fg));
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
.fw-sc-icon-btn:disabled {
|
|
659
|
+
opacity: 0.5;
|
|
660
|
+
cursor: not-allowed;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.fw-sc-icon-btn--active {
|
|
664
|
+
color: hsl(var(--tn-blue));
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.fw-sc-icon-btn--inactive {
|
|
668
|
+
opacity: 0.45;
|
|
669
|
+
color: hsl(var(--tn-comment));
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.fw-sc-icon-btn--primary {
|
|
673
|
+
color: hsl(var(--tn-green));
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.fw-sc-icon-btn--primary:disabled {
|
|
677
|
+
color: hsl(var(--tn-green));
|
|
678
|
+
opacity: 1;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.fw-sc-icon-btn--destructive:hover {
|
|
682
|
+
color: hsl(var(--tn-red));
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.fw-sc-icon-btn--muted {
|
|
686
|
+
color: hsl(var(--tn-comment) / 0.5);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.fw-sc-icon-btn svg {
|
|
690
|
+
width: 16px;
|
|
691
|
+
height: 16px;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/* =============================================
|
|
695
|
+
Settings Panel
|
|
696
|
+
============================================= */
|
|
697
|
+
.fw-sc-settings {
|
|
698
|
+
display: flex;
|
|
699
|
+
flex-direction: column;
|
|
700
|
+
gap: 0.5rem;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.fw-sc-setting-row {
|
|
704
|
+
display: flex;
|
|
705
|
+
align-items: center;
|
|
706
|
+
justify-content: space-between;
|
|
707
|
+
gap: 1rem;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
.fw-sc-setting-label {
|
|
711
|
+
font-size: 0.875rem;
|
|
712
|
+
color: hsl(var(--tn-fg-dark));
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
.fw-sc-select {
|
|
716
|
+
padding: 0.25rem 0.5rem;
|
|
717
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
718
|
+
border-radius: 4px;
|
|
719
|
+
background: hsl(var(--tn-bg-highlight));
|
|
720
|
+
color: hsl(var(--tn-fg));
|
|
721
|
+
font-size: 0.875rem;
|
|
722
|
+
cursor: pointer;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
.fw-sc-select:focus {
|
|
726
|
+
outline: none;
|
|
727
|
+
border-color: hsl(var(--tn-blue));
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/* =============================================
|
|
731
|
+
Action Bar (Bottom Controls)
|
|
732
|
+
============================================= */
|
|
733
|
+
.fw-sc-actions {
|
|
734
|
+
display: flex;
|
|
735
|
+
border-top: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
.fw-sc-actions button {
|
|
739
|
+
padding: 1rem;
|
|
740
|
+
border: none;
|
|
741
|
+
border-radius: 0;
|
|
742
|
+
background: transparent;
|
|
743
|
+
color: hsl(var(--tn-fg));
|
|
744
|
+
font-size: 0.875rem;
|
|
745
|
+
font-weight: 500;
|
|
746
|
+
cursor: pointer;
|
|
747
|
+
transition: background 0.15s;
|
|
748
|
+
border-right: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
749
|
+
display: flex;
|
|
750
|
+
align-items: center;
|
|
751
|
+
justify-content: center;
|
|
752
|
+
gap: 0.25rem;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
.fw-sc-actions button:last-child {
|
|
756
|
+
border-right: none;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
.fw-sc-actions button:hover:not(:disabled) {
|
|
760
|
+
background: hsl(var(--tn-bg-highlight) / 0.5);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
.fw-sc-actions button:disabled {
|
|
764
|
+
color: hsl(var(--tn-comment));
|
|
765
|
+
cursor: not-allowed;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
.fw-sc-actions button svg {
|
|
769
|
+
width: 18px;
|
|
770
|
+
height: 18px;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/* Secondary actions (Camera, Screen, Settings) - smaller */
|
|
774
|
+
.fw-sc-action-secondary {
|
|
775
|
+
flex: 0 0 auto;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
.fw-sc-action-secondary--active {
|
|
779
|
+
background: hsl(var(--tn-blue) / 0.2) !important;
|
|
780
|
+
color: hsl(var(--tn-blue)) !important;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/* Settings icon rotation on hover */
|
|
784
|
+
.fw-sc-action-secondary .settings-icon-wrapper {
|
|
785
|
+
display: inline-flex;
|
|
786
|
+
transition: transform 0.2s ease;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.fw-sc-action-secondary:hover .settings-icon-wrapper {
|
|
790
|
+
transform: rotate(90deg);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/* Primary action (Go Live) - takes remaining space */
|
|
794
|
+
.fw-sc-action-primary {
|
|
795
|
+
flex: 1;
|
|
796
|
+
font-weight: 600 !important;
|
|
797
|
+
background: hsl(var(--tn-red)) !important;
|
|
798
|
+
color: white !important;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.fw-sc-action-primary:hover:not(:disabled) {
|
|
802
|
+
background: hsl(var(--tn-red) / 0.8) !important;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
.fw-sc-action-primary:disabled {
|
|
806
|
+
background: hsl(var(--tn-bg-highlight)) !important;
|
|
807
|
+
color: hsl(var(--tn-comment)) !important;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/* Stop action (when streaming) */
|
|
811
|
+
.fw-sc-action-stop {
|
|
812
|
+
background: hsl(var(--tn-bg-highlight)) !important;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
.fw-sc-action-stop:hover:not(:disabled) {
|
|
816
|
+
background: hsl(var(--tn-red) / 0.3) !important;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/* =============================================
|
|
820
|
+
Status Badge
|
|
821
|
+
============================================= */
|
|
822
|
+
.fw-sc-badge {
|
|
823
|
+
display: inline-flex;
|
|
824
|
+
align-items: center;
|
|
825
|
+
gap: 0.25rem;
|
|
826
|
+
padding: 0.125rem 0.5rem;
|
|
827
|
+
font-size: 0.7rem;
|
|
828
|
+
font-weight: 600;
|
|
829
|
+
text-transform: uppercase;
|
|
830
|
+
letter-spacing: 0.025em;
|
|
831
|
+
border-radius: 2px;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
.fw-sc-badge--idle {
|
|
835
|
+
background: hsl(var(--tn-bg-highlight));
|
|
836
|
+
color: hsl(var(--tn-comment));
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
.fw-sc-badge--ready {
|
|
840
|
+
background: hsl(var(--tn-blue) / 0.2);
|
|
841
|
+
color: hsl(var(--tn-blue));
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
.fw-sc-badge--live {
|
|
845
|
+
background: hsl(var(--tn-red));
|
|
846
|
+
color: white;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.fw-sc-badge--connecting {
|
|
850
|
+
background: hsl(var(--tn-yellow) / 0.2);
|
|
851
|
+
color: hsl(var(--tn-yellow));
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
.fw-sc-badge--error {
|
|
855
|
+
background: hsl(var(--tn-red) / 0.2);
|
|
856
|
+
color: hsl(var(--tn-red));
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/* =============================================
|
|
860
|
+
Volume Slider
|
|
861
|
+
============================================= */
|
|
862
|
+
.fw-sc-volume-label {
|
|
863
|
+
font-size: 0.65rem;
|
|
864
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
865
|
+
color: hsl(var(--tn-fg-dark));
|
|
866
|
+
min-width: 32px;
|
|
867
|
+
text-align: right;
|
|
868
|
+
font-variant-numeric: tabular-nums;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
.fw-sc-volume-slider {
|
|
872
|
+
width: 80px; /* Slightly wider by default */
|
|
873
|
+
height: 6px;
|
|
874
|
+
-webkit-appearance: none;
|
|
875
|
+
appearance: none;
|
|
876
|
+
background: hsl(var(--tn-bg-highlight));
|
|
877
|
+
border-radius: 3px;
|
|
878
|
+
cursor: pointer;
|
|
879
|
+
position: relative;
|
|
880
|
+
transition: opacity 0.2s;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
.fw-sc-volume-slider:hover {
|
|
884
|
+
opacity: 0.9;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/* Track fill simulation (Note: WebKit requires JS to update background-size for true fill before thumb,
|
|
888
|
+
but we can use a gradient trick if value is known, or just keep the clean track look) */
|
|
889
|
+
|
|
890
|
+
.fw-sc-volume-slider::-webkit-slider-thumb {
|
|
891
|
+
-webkit-appearance: none;
|
|
892
|
+
width: 14px;
|
|
893
|
+
height: 14px;
|
|
894
|
+
border-radius: 50%;
|
|
895
|
+
background: hsl(var(--tn-fg));
|
|
896
|
+
border: 2px solid hsl(var(--tn-bg)); /* Ring effect */
|
|
897
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
898
|
+
cursor: pointer;
|
|
899
|
+
margin-top: -4px; /* Center thumb on 6px track */
|
|
900
|
+
transition: transform 0.1s;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
.fw-sc-volume-slider:hover::-webkit-slider-thumb {
|
|
904
|
+
transform: scale(1.1);
|
|
905
|
+
background: white;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
.fw-sc-volume-slider::-moz-range-thumb {
|
|
909
|
+
width: 14px;
|
|
910
|
+
height: 14px;
|
|
911
|
+
border-radius: 50%;
|
|
912
|
+
background: hsl(var(--tn-fg));
|
|
913
|
+
border: 2px solid hsl(var(--tn-bg));
|
|
914
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
915
|
+
cursor: pointer;
|
|
916
|
+
transition: transform 0.1s;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
.fw-sc-volume-slider:hover::-moz-range-thumb {
|
|
920
|
+
transform: scale(1.1);
|
|
921
|
+
background: white;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/* Boosted state (>100%) - Add a glow */
|
|
925
|
+
.fw-sc-volume-slider--boosted::-webkit-slider-thumb {
|
|
926
|
+
background: hsl(var(--tn-yellow));
|
|
927
|
+
box-shadow: 0 0 5px hsl(var(--tn-yellow) / 0.5);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
.fw-sc-volume-slider--boosted::-moz-range-thumb {
|
|
931
|
+
background: hsl(var(--tn-yellow));
|
|
932
|
+
box-shadow: 0 0 5px hsl(var(--tn-yellow) / 0.5);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/* =============================================
|
|
936
|
+
Error State
|
|
937
|
+
============================================= */
|
|
938
|
+
.fw-sc-error {
|
|
939
|
+
padding: 1rem;
|
|
940
|
+
background: hsl(var(--tn-red) / 0.1);
|
|
941
|
+
border-left: 3px solid hsl(var(--tn-red));
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
.fw-sc-error-title {
|
|
945
|
+
font-weight: 600;
|
|
946
|
+
color: hsl(var(--tn-red));
|
|
947
|
+
margin-bottom: 0.25rem;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
.fw-sc-error-message {
|
|
951
|
+
font-size: 0.875rem;
|
|
952
|
+
color: hsl(var(--tn-fg-dark));
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/* =============================================
|
|
956
|
+
Utility Classes
|
|
957
|
+
============================================= */
|
|
958
|
+
.fw-sc-hidden {
|
|
959
|
+
display: none !important;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
.fw-sc-sr-only {
|
|
963
|
+
position: absolute;
|
|
964
|
+
width: 1px;
|
|
965
|
+
height: 1px;
|
|
966
|
+
padding: 0;
|
|
967
|
+
margin: -1px;
|
|
968
|
+
overflow: hidden;
|
|
969
|
+
clip: rect(0, 0, 0, 0);
|
|
970
|
+
white-space: nowrap;
|
|
971
|
+
border: 0;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/* =============================================
|
|
975
|
+
Settings Dropdown
|
|
976
|
+
============================================= */
|
|
977
|
+
.fw-sc-settings-dropdown {
|
|
978
|
+
position: absolute;
|
|
979
|
+
top: calc(100% + 4px);
|
|
980
|
+
right: 0;
|
|
981
|
+
min-width: 200px;
|
|
982
|
+
background: hsl(var(--tn-bg-dark));
|
|
983
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
984
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
985
|
+
z-index: 50;
|
|
986
|
+
overflow: hidden;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
.fw-sc-dropdown-section {
|
|
990
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
.fw-sc-dropdown-section:last-child {
|
|
994
|
+
border-bottom: none;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
.fw-sc-dropdown-label {
|
|
998
|
+
padding: 0.5rem 0.75rem 0.25rem;
|
|
999
|
+
font-size: 0.65rem;
|
|
1000
|
+
font-weight: 600;
|
|
1001
|
+
text-transform: uppercase;
|
|
1002
|
+
letter-spacing: 0.05em;
|
|
1003
|
+
color: hsl(var(--tn-comment));
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
.fw-sc-dropdown-options {
|
|
1007
|
+
display: flex;
|
|
1008
|
+
flex-direction: column;
|
|
1009
|
+
padding: 0 0.25rem 0.5rem;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
.fw-sc-dropdown-option {
|
|
1013
|
+
display: flex;
|
|
1014
|
+
flex-direction: column;
|
|
1015
|
+
align-items: flex-start;
|
|
1016
|
+
gap: 0;
|
|
1017
|
+
padding: 0.375rem 0.5rem;
|
|
1018
|
+
margin: 0;
|
|
1019
|
+
border: none;
|
|
1020
|
+
border-radius: 4px;
|
|
1021
|
+
background: transparent;
|
|
1022
|
+
color: hsl(var(--tn-fg));
|
|
1023
|
+
cursor: pointer;
|
|
1024
|
+
text-align: left;
|
|
1025
|
+
width: 100%;
|
|
1026
|
+
transition: background 0.15s;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
.fw-sc-dropdown-option:hover:not(:disabled) {
|
|
1030
|
+
background: hsl(var(--tn-bg-highlight) / 0.5);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
.fw-sc-dropdown-option:disabled {
|
|
1034
|
+
opacity: 0.5;
|
|
1035
|
+
cursor: not-allowed;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
.fw-sc-dropdown-option--active {
|
|
1039
|
+
background: hsl(var(--tn-blue) / 0.2);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
.fw-sc-dropdown-option--active:hover:not(:disabled) {
|
|
1043
|
+
background: hsl(var(--tn-blue) / 0.3);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
.fw-sc-dropdown-option-label {
|
|
1047
|
+
font-size: 0.875rem;
|
|
1048
|
+
font-weight: 500;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
.fw-sc-dropdown-option-desc {
|
|
1052
|
+
font-size: 0.7rem;
|
|
1053
|
+
color: hsl(var(--tn-comment));
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
.fw-sc-dropdown-info {
|
|
1057
|
+
padding: 0.25rem 0.75rem 0.5rem;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
.fw-sc-dropdown-info-row {
|
|
1061
|
+
display: flex;
|
|
1062
|
+
justify-content: space-between;
|
|
1063
|
+
align-items: center;
|
|
1064
|
+
padding: 0.25rem 0;
|
|
1065
|
+
font-size: 0.75rem;
|
|
1066
|
+
color: hsl(var(--tn-fg-dark));
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
.fw-sc-dropdown-info-value {
|
|
1070
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
1071
|
+
color: hsl(var(--tn-fg));
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
/* =============================================
|
|
1075
|
+
Context Menu
|
|
1076
|
+
============================================= */
|
|
1077
|
+
.fw-sc-context-menu {
|
|
1078
|
+
position: fixed;
|
|
1079
|
+
min-width: 160px;
|
|
1080
|
+
background: hsl(var(--tn-bg-dark));
|
|
1081
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
1082
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
1083
|
+
z-index: 100;
|
|
1084
|
+
overflow: hidden;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
.fw-sc-context-menu-item {
|
|
1088
|
+
display: flex;
|
|
1089
|
+
align-items: center;
|
|
1090
|
+
gap: 0.5rem;
|
|
1091
|
+
padding: 0.5rem 0.75rem;
|
|
1092
|
+
font-size: 0.875rem;
|
|
1093
|
+
color: hsl(var(--tn-fg));
|
|
1094
|
+
cursor: pointer;
|
|
1095
|
+
background: transparent;
|
|
1096
|
+
border: none;
|
|
1097
|
+
width: 100%;
|
|
1098
|
+
text-align: left;
|
|
1099
|
+
transition:
|
|
1100
|
+
background 0.15s,
|
|
1101
|
+
color 0.15s;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
.fw-sc-context-menu-item:hover {
|
|
1105
|
+
background: hsl(var(--tn-bg-highlight) / 0.7);
|
|
1106
|
+
color: hsl(var(--tn-fg));
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
.fw-sc-context-menu-item--destructive {
|
|
1110
|
+
color: hsl(var(--tn-red));
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
.fw-sc-context-menu-item--destructive:hover {
|
|
1114
|
+
background: hsl(var(--tn-red) / 0.15);
|
|
1115
|
+
color: hsl(var(--tn-red));
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
.fw-sc-context-menu-separator {
|
|
1119
|
+
height: 1px;
|
|
1120
|
+
background: hsl(var(--tn-fg-gutter) / 0.3);
|
|
1121
|
+
margin: 0.25rem 0;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
.fw-sc-context-menu-label {
|
|
1125
|
+
padding: 0.375rem 0.75rem;
|
|
1126
|
+
font-size: 0.65rem;
|
|
1127
|
+
font-weight: 600;
|
|
1128
|
+
text-transform: uppercase;
|
|
1129
|
+
letter-spacing: 0.05em;
|
|
1130
|
+
color: hsl(var(--tn-comment));
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/* =============================================
|
|
1134
|
+
Compositor Controls (Phase 3)
|
|
1135
|
+
============================================= */
|
|
1136
|
+
.fw-sc-compositor-controls {
|
|
1137
|
+
display: flex;
|
|
1138
|
+
flex-direction: column;
|
|
1139
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
.fw-sc-compositor-controls--disabled,
|
|
1143
|
+
.fw-sc-compositor-controls--loading {
|
|
1144
|
+
padding: 1rem;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
.fw-sc-compositor-enable {
|
|
1148
|
+
text-align: center;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
.fw-sc-compositor-enable p {
|
|
1152
|
+
font-size: 0.875rem;
|
|
1153
|
+
color: hsl(var(--tn-fg-dark));
|
|
1154
|
+
margin-bottom: 0.75rem;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
.fw-sc-compositor-enable-btn {
|
|
1158
|
+
padding: 0.5rem 1rem;
|
|
1159
|
+
border: 1px solid hsl(var(--tn-blue));
|
|
1160
|
+
border-radius: 4px;
|
|
1161
|
+
background: hsl(var(--tn-blue) / 0.1);
|
|
1162
|
+
color: hsl(var(--tn-blue));
|
|
1163
|
+
font-size: 0.875rem;
|
|
1164
|
+
font-weight: 500;
|
|
1165
|
+
cursor: pointer;
|
|
1166
|
+
transition: background 0.15s;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
.fw-sc-compositor-enable-btn:hover {
|
|
1170
|
+
background: hsl(var(--tn-blue) / 0.2);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
.fw-sc-compositor-loading {
|
|
1174
|
+
text-align: center;
|
|
1175
|
+
color: hsl(var(--tn-comment));
|
|
1176
|
+
font-size: 0.875rem;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
.fw-sc-compositor-header {
|
|
1180
|
+
display: flex;
|
|
1181
|
+
align-items: center;
|
|
1182
|
+
justify-content: space-between;
|
|
1183
|
+
padding: 0.5rem 1rem;
|
|
1184
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
|
|
1185
|
+
background: hsl(var(--tn-bg-highlight) / 0.3);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
.fw-sc-compositor-info {
|
|
1189
|
+
display: flex;
|
|
1190
|
+
align-items: center;
|
|
1191
|
+
gap: 0.75rem;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
.fw-sc-compositor-title {
|
|
1195
|
+
font-size: 0.7rem;
|
|
1196
|
+
font-weight: 600;
|
|
1197
|
+
text-transform: uppercase;
|
|
1198
|
+
letter-spacing: 0.05em;
|
|
1199
|
+
color: hsl(var(--tn-comment));
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
.fw-sc-compositor-renderer {
|
|
1203
|
+
font-size: 0.7rem;
|
|
1204
|
+
color: hsl(var(--tn-fg-dark));
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
.fw-sc-compositor-stats-inline {
|
|
1208
|
+
font-size: 0.65rem;
|
|
1209
|
+
color: hsl(var(--tn-fg-gutter));
|
|
1210
|
+
font-family:
|
|
1211
|
+
ui-monospace,
|
|
1212
|
+
SFMono-Regular,
|
|
1213
|
+
SF Mono,
|
|
1214
|
+
Menlo,
|
|
1215
|
+
Consolas,
|
|
1216
|
+
monospace;
|
|
1217
|
+
margin-left: 0.5rem;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
.fw-sc-compositor-disable-btn {
|
|
1221
|
+
width: 24px;
|
|
1222
|
+
height: 24px;
|
|
1223
|
+
display: flex;
|
|
1224
|
+
align-items: center;
|
|
1225
|
+
justify-content: center;
|
|
1226
|
+
padding: 0;
|
|
1227
|
+
border: none;
|
|
1228
|
+
border-radius: 4px;
|
|
1229
|
+
background: transparent;
|
|
1230
|
+
color: hsl(var(--tn-comment));
|
|
1231
|
+
cursor: pointer;
|
|
1232
|
+
transition: all 0.15s;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
.fw-sc-compositor-disable-btn:hover {
|
|
1236
|
+
background: hsl(var(--tn-red) / 0.2);
|
|
1237
|
+
color: hsl(var(--tn-red));
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
.fw-sc-compositor-stats {
|
|
1241
|
+
display: flex;
|
|
1242
|
+
align-items: center;
|
|
1243
|
+
gap: 1rem;
|
|
1244
|
+
padding: 0.25rem 1rem;
|
|
1245
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
1246
|
+
font-size: 0.7rem;
|
|
1247
|
+
color: hsl(var(--tn-fg-dark));
|
|
1248
|
+
background: hsl(var(--tn-bg-dark) / 0.5);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
.fw-sc-stat {
|
|
1252
|
+
color: hsl(var(--tn-cyan));
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
/* =============================================
|
|
1256
|
+
Compositor Actions (Header Right Side)
|
|
1257
|
+
============================================= */
|
|
1258
|
+
.fw-sc-compositor-actions {
|
|
1259
|
+
display: flex;
|
|
1260
|
+
align-items: center;
|
|
1261
|
+
gap: 0.5rem;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
.fw-sc-scaling-mode-select {
|
|
1265
|
+
padding: 0.25rem 0.5rem;
|
|
1266
|
+
font-size: 0.7rem;
|
|
1267
|
+
background: hsl(var(--tn-bg-dark));
|
|
1268
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
1269
|
+
border-radius: 4px;
|
|
1270
|
+
color: hsl(var(--tn-fg));
|
|
1271
|
+
cursor: pointer;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
.fw-sc-scaling-mode-select:hover {
|
|
1275
|
+
border-color: hsl(var(--tn-fg-gutter) / 0.5);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
/* =============================================
|
|
1279
|
+
Compositor Sources (Simplified)
|
|
1280
|
+
============================================= */
|
|
1281
|
+
.fw-sc-compositor-sources {
|
|
1282
|
+
padding: 0.5rem;
|
|
1283
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
.fw-sc-section-label {
|
|
1287
|
+
font-size: 0.65rem;
|
|
1288
|
+
font-weight: 600;
|
|
1289
|
+
text-transform: uppercase;
|
|
1290
|
+
letter-spacing: 0.05em;
|
|
1291
|
+
color: hsl(var(--tn-comment));
|
|
1292
|
+
margin-bottom: 0.5rem;
|
|
1293
|
+
padding-left: 0.5rem;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
.fw-sc-source-list {
|
|
1297
|
+
display: flex;
|
|
1298
|
+
flex-direction: column;
|
|
1299
|
+
gap: 0.25rem;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
.fw-sc-source-row {
|
|
1303
|
+
display: flex;
|
|
1304
|
+
align-items: center;
|
|
1305
|
+
gap: 0.5rem;
|
|
1306
|
+
padding: 0.375rem 0.5rem;
|
|
1307
|
+
background: hsl(var(--tn-bg-highlight) / 0.3);
|
|
1308
|
+
border-radius: 4px;
|
|
1309
|
+
transition: opacity 0.15s;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
.fw-sc-source-row--hidden {
|
|
1313
|
+
opacity: 0.4;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
.fw-sc-visibility-btn {
|
|
1317
|
+
width: 24px;
|
|
1318
|
+
height: 24px;
|
|
1319
|
+
display: flex;
|
|
1320
|
+
align-items: center;
|
|
1321
|
+
justify-content: center;
|
|
1322
|
+
padding: 0;
|
|
1323
|
+
border: none;
|
|
1324
|
+
border-radius: 4px;
|
|
1325
|
+
background: transparent;
|
|
1326
|
+
color: hsl(var(--tn-fg-dark));
|
|
1327
|
+
cursor: pointer;
|
|
1328
|
+
transition: all 0.15s;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
.fw-sc-visibility-btn:hover {
|
|
1332
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1333
|
+
color: hsl(var(--tn-fg));
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
.fw-sc-source-icon {
|
|
1337
|
+
display: flex;
|
|
1338
|
+
align-items: center;
|
|
1339
|
+
color: hsl(var(--tn-comment));
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
.fw-sc-source-label {
|
|
1343
|
+
flex: 1;
|
|
1344
|
+
font-size: 0.8rem;
|
|
1345
|
+
color: hsl(var(--tn-fg));
|
|
1346
|
+
overflow: hidden;
|
|
1347
|
+
text-overflow: ellipsis;
|
|
1348
|
+
white-space: nowrap;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/* =============================================
|
|
1352
|
+
Layout Presets (Grid)
|
|
1353
|
+
============================================= */
|
|
1354
|
+
.fw-sc-layout-presets {
|
|
1355
|
+
padding: 0.5rem;
|
|
1356
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
.fw-sc-layout-grid {
|
|
1360
|
+
display: grid;
|
|
1361
|
+
grid-template-columns: repeat(6, 1fr);
|
|
1362
|
+
gap: 0.25rem;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
.fw-sc-layout-btn {
|
|
1366
|
+
aspect-ratio: 1;
|
|
1367
|
+
display: flex;
|
|
1368
|
+
align-items: center;
|
|
1369
|
+
justify-content: center;
|
|
1370
|
+
padding: 0;
|
|
1371
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
1372
|
+
border-radius: 4px;
|
|
1373
|
+
background: hsl(var(--tn-bg-highlight) / 0.3);
|
|
1374
|
+
color: hsl(var(--tn-fg-dark));
|
|
1375
|
+
cursor: pointer;
|
|
1376
|
+
transition: all 0.15s;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
.fw-sc-layout-btn:hover {
|
|
1380
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1381
|
+
border-color: hsl(var(--tn-fg-gutter) / 0.5);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
.fw-sc-layout-btn--active {
|
|
1385
|
+
background: hsl(var(--tn-blue) / 0.2);
|
|
1386
|
+
border-color: hsl(var(--tn-blue) / 0.5);
|
|
1387
|
+
color: hsl(var(--tn-blue));
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
.fw-sc-layout-btn--disabled,
|
|
1391
|
+
.fw-sc-layout-btn:disabled {
|
|
1392
|
+
opacity: 0.3;
|
|
1393
|
+
cursor: not-allowed;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
.fw-sc-layout-btn--disabled:hover,
|
|
1397
|
+
.fw-sc-layout-btn:disabled:hover {
|
|
1398
|
+
background: hsl(var(--tn-bg-highlight) / 0.3);
|
|
1399
|
+
border-color: hsl(var(--tn-fg-gutter) / 0.3);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/* Legacy layout preset buttons (for backwards compat) */
|
|
1403
|
+
.fw-sc-layout-presets-label {
|
|
1404
|
+
font-size: 0.7rem;
|
|
1405
|
+
font-weight: 600;
|
|
1406
|
+
text-transform: uppercase;
|
|
1407
|
+
letter-spacing: 0.05em;
|
|
1408
|
+
color: hsl(var(--tn-comment));
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
.fw-sc-layout-preset-buttons {
|
|
1412
|
+
display: flex;
|
|
1413
|
+
gap: 0.25rem;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
.fw-sc-layout-preset-btn {
|
|
1417
|
+
width: 32px;
|
|
1418
|
+
height: 28px;
|
|
1419
|
+
display: flex;
|
|
1420
|
+
align-items: center;
|
|
1421
|
+
justify-content: center;
|
|
1422
|
+
padding: 0;
|
|
1423
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
1424
|
+
border-radius: 4px;
|
|
1425
|
+
background: hsl(var(--tn-bg-highlight) / 0.3);
|
|
1426
|
+
color: hsl(var(--tn-fg-dark));
|
|
1427
|
+
font-size: 0.75rem;
|
|
1428
|
+
cursor: pointer;
|
|
1429
|
+
transition: all 0.15s;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
.fw-sc-layout-preset-btn:hover {
|
|
1433
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1434
|
+
border-color: hsl(var(--tn-fg-gutter) / 0.5);
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
.fw-sc-layout-preset-btn--active {
|
|
1438
|
+
background: hsl(var(--tn-blue) / 0.2);
|
|
1439
|
+
border-color: hsl(var(--tn-blue) / 0.5);
|
|
1440
|
+
color: hsl(var(--tn-blue));
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
.fw-sc-layout-preset-btn--disabled,
|
|
1444
|
+
.fw-sc-layout-preset-btn:disabled {
|
|
1445
|
+
opacity: 0.3;
|
|
1446
|
+
cursor: not-allowed;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
.fw-sc-layout-preset-btn--disabled:hover,
|
|
1450
|
+
.fw-sc-layout-preset-btn:disabled:hover {
|
|
1451
|
+
background: transparent;
|
|
1452
|
+
border-color: hsl(var(--tn-fg-gutter) / 0.3);
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
/* =============================================
|
|
1456
|
+
Scene Switcher
|
|
1457
|
+
============================================= */
|
|
1458
|
+
.fw-sc-scene-switcher {
|
|
1459
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
.fw-sc-scene-switcher-header {
|
|
1463
|
+
display: flex;
|
|
1464
|
+
align-items: center;
|
|
1465
|
+
justify-content: space-between;
|
|
1466
|
+
padding: 0.5rem 1rem;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
.fw-sc-scene-switcher-title {
|
|
1470
|
+
font-size: 0.7rem;
|
|
1471
|
+
font-weight: 600;
|
|
1472
|
+
text-transform: uppercase;
|
|
1473
|
+
letter-spacing: 0.05em;
|
|
1474
|
+
color: hsl(var(--tn-comment));
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
.fw-sc-transition-controls {
|
|
1478
|
+
display: flex;
|
|
1479
|
+
align-items: center;
|
|
1480
|
+
gap: 0.5rem;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
.fw-sc-transition-select {
|
|
1484
|
+
padding: 0.25rem 0.5rem;
|
|
1485
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
1486
|
+
border-radius: 4px;
|
|
1487
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1488
|
+
color: hsl(var(--tn-fg));
|
|
1489
|
+
font-size: 0.75rem;
|
|
1490
|
+
cursor: pointer;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
.fw-sc-transition-select:focus {
|
|
1494
|
+
outline: none;
|
|
1495
|
+
border-color: hsl(var(--tn-blue));
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
.fw-sc-transition-duration {
|
|
1499
|
+
width: 60px;
|
|
1500
|
+
padding: 0.25rem 0.5rem;
|
|
1501
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
1502
|
+
border-radius: 4px;
|
|
1503
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1504
|
+
color: hsl(var(--tn-fg));
|
|
1505
|
+
font-size: 0.75rem;
|
|
1506
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
.fw-sc-transition-duration:focus {
|
|
1510
|
+
outline: none;
|
|
1511
|
+
border-color: hsl(var(--tn-blue));
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
.fw-sc-transition-unit {
|
|
1515
|
+
font-size: 0.7rem;
|
|
1516
|
+
color: hsl(var(--tn-comment));
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
.fw-sc-scene-list {
|
|
1520
|
+
display: flex;
|
|
1521
|
+
gap: 0.5rem;
|
|
1522
|
+
padding: 0 1rem 0.75rem;
|
|
1523
|
+
overflow-x: auto;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
.fw-sc-scene-item {
|
|
1527
|
+
position: relative;
|
|
1528
|
+
display: flex;
|
|
1529
|
+
flex-direction: column;
|
|
1530
|
+
align-items: flex-start;
|
|
1531
|
+
padding: 0.5rem 0.75rem;
|
|
1532
|
+
min-width: 100px;
|
|
1533
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
1534
|
+
border-radius: 4px;
|
|
1535
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1536
|
+
color: hsl(var(--tn-fg));
|
|
1537
|
+
cursor: pointer;
|
|
1538
|
+
transition: all 0.15s;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
.fw-sc-scene-item:hover {
|
|
1542
|
+
border-color: hsl(var(--tn-fg-gutter) / 0.5);
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
.fw-sc-scene-item--active {
|
|
1546
|
+
border-color: hsl(var(--tn-green));
|
|
1547
|
+
box-shadow: 0 0 0 1px hsl(var(--tn-green) / 0.3);
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
.fw-sc-scene-item--transitioning {
|
|
1551
|
+
opacity: 0.7;
|
|
1552
|
+
pointer-events: none;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
.fw-sc-scene-name {
|
|
1556
|
+
font-size: 0.875rem;
|
|
1557
|
+
font-weight: 500;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
.fw-sc-scene-layer-count {
|
|
1561
|
+
font-size: 0.7rem;
|
|
1562
|
+
color: hsl(var(--tn-comment));
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
.fw-sc-scene-delete {
|
|
1566
|
+
position: absolute;
|
|
1567
|
+
top: 2px;
|
|
1568
|
+
right: 2px;
|
|
1569
|
+
width: 18px;
|
|
1570
|
+
height: 18px;
|
|
1571
|
+
display: flex;
|
|
1572
|
+
align-items: center;
|
|
1573
|
+
justify-content: center;
|
|
1574
|
+
padding: 0;
|
|
1575
|
+
border: none;
|
|
1576
|
+
border-radius: 2px;
|
|
1577
|
+
background: transparent;
|
|
1578
|
+
color: hsl(var(--tn-comment));
|
|
1579
|
+
font-size: 14px;
|
|
1580
|
+
cursor: pointer;
|
|
1581
|
+
opacity: 0;
|
|
1582
|
+
transition: all 0.15s;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
.fw-sc-scene-item:hover .fw-sc-scene-delete {
|
|
1586
|
+
opacity: 1;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
.fw-sc-scene-delete:hover {
|
|
1590
|
+
background: hsl(var(--tn-red) / 0.2);
|
|
1591
|
+
color: hsl(var(--tn-red));
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
.fw-sc-scene-add {
|
|
1595
|
+
display: flex;
|
|
1596
|
+
align-items: center;
|
|
1597
|
+
justify-content: center;
|
|
1598
|
+
min-width: 40px;
|
|
1599
|
+
padding: 0.5rem;
|
|
1600
|
+
border: 1px dashed hsl(var(--tn-fg-gutter) / 0.5);
|
|
1601
|
+
border-radius: 4px;
|
|
1602
|
+
background: transparent;
|
|
1603
|
+
color: hsl(var(--tn-comment));
|
|
1604
|
+
font-size: 1.25rem;
|
|
1605
|
+
cursor: pointer;
|
|
1606
|
+
transition: all 0.15s;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
.fw-sc-scene-add:hover {
|
|
1610
|
+
border-color: hsl(var(--tn-blue));
|
|
1611
|
+
color: hsl(var(--tn-blue));
|
|
1612
|
+
background: hsl(var(--tn-blue) / 0.1);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
/* New Scene Input */
|
|
1616
|
+
.fw-sc-new-scene-input {
|
|
1617
|
+
display: flex;
|
|
1618
|
+
gap: 0.5rem;
|
|
1619
|
+
padding: 0.5rem 1rem;
|
|
1620
|
+
background: hsl(var(--tn-bg-dark) / 0.5);
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
.fw-sc-new-scene-input input {
|
|
1624
|
+
flex: 1;
|
|
1625
|
+
padding: 0.375rem 0.5rem;
|
|
1626
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
1627
|
+
border-radius: 4px;
|
|
1628
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1629
|
+
color: hsl(var(--tn-fg));
|
|
1630
|
+
font-size: 0.875rem;
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
.fw-sc-new-scene-input input:focus {
|
|
1634
|
+
outline: none;
|
|
1635
|
+
border-color: hsl(var(--tn-blue));
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
.fw-sc-new-scene-input button {
|
|
1639
|
+
padding: 0.375rem 0.75rem;
|
|
1640
|
+
border: none;
|
|
1641
|
+
border-radius: 4px;
|
|
1642
|
+
background: hsl(var(--tn-blue));
|
|
1643
|
+
color: white;
|
|
1644
|
+
font-size: 0.75rem;
|
|
1645
|
+
font-weight: 500;
|
|
1646
|
+
cursor: pointer;
|
|
1647
|
+
transition: background 0.15s;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
.fw-sc-new-scene-input button:hover {
|
|
1651
|
+
background: hsl(var(--tn-blue) / 0.8);
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
.fw-sc-new-scene-input button:last-child {
|
|
1655
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1656
|
+
color: hsl(var(--tn-fg-dark));
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
.fw-sc-new-scene-input button:last-child:hover {
|
|
1660
|
+
background: hsl(var(--tn-bg-highlight) / 0.8);
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
/* =============================================
|
|
1664
|
+
Layer List
|
|
1665
|
+
============================================= */
|
|
1666
|
+
.fw-sc-layer-list {
|
|
1667
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
.fw-sc-layer-list-header {
|
|
1671
|
+
display: flex;
|
|
1672
|
+
align-items: center;
|
|
1673
|
+
justify-content: space-between;
|
|
1674
|
+
padding: 0.5rem 1rem;
|
|
1675
|
+
background: hsl(var(--tn-bg-highlight) / 0.3);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
.fw-sc-layer-list-title {
|
|
1679
|
+
font-size: 0.7rem;
|
|
1680
|
+
font-weight: 600;
|
|
1681
|
+
text-transform: uppercase;
|
|
1682
|
+
letter-spacing: 0.05em;
|
|
1683
|
+
color: hsl(var(--tn-comment));
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
.fw-sc-layer-count {
|
|
1687
|
+
font-size: 0.7rem;
|
|
1688
|
+
color: hsl(var(--tn-fg-dark));
|
|
1689
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1690
|
+
padding: 0.125rem 0.375rem;
|
|
1691
|
+
border-radius: 8px;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
.fw-sc-layer-items {
|
|
1695
|
+
display: flex;
|
|
1696
|
+
flex-direction: column;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
.fw-sc-layer-empty {
|
|
1700
|
+
padding: 1rem;
|
|
1701
|
+
text-align: center;
|
|
1702
|
+
color: hsl(var(--tn-comment));
|
|
1703
|
+
font-size: 0.875rem;
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
.fw-sc-layer-item {
|
|
1707
|
+
display: flex;
|
|
1708
|
+
align-items: center;
|
|
1709
|
+
gap: 0.5rem;
|
|
1710
|
+
padding: 0.5rem 1rem;
|
|
1711
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.15);
|
|
1712
|
+
cursor: pointer;
|
|
1713
|
+
transition: background 0.15s;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
.fw-sc-layer-item:last-child {
|
|
1717
|
+
border-bottom: none;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
.fw-sc-layer-item:hover {
|
|
1721
|
+
background: hsl(var(--tn-bg-highlight) / 0.3);
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
.fw-sc-layer-item--selected {
|
|
1725
|
+
background: hsl(var(--tn-blue) / 0.1);
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
.fw-sc-layer-item--selected:hover {
|
|
1729
|
+
background: hsl(var(--tn-blue) / 0.15);
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
.fw-sc-layer-item--dragging {
|
|
1733
|
+
opacity: 0.5;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
.fw-sc-layer-item--drag-over {
|
|
1737
|
+
border-top: 2px solid hsl(var(--tn-blue));
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
.fw-sc-layer-item--hidden {
|
|
1741
|
+
opacity: 0.5;
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
.fw-sc-layer-visibility {
|
|
1745
|
+
width: 24px;
|
|
1746
|
+
height: 24px;
|
|
1747
|
+
display: flex;
|
|
1748
|
+
align-items: center;
|
|
1749
|
+
justify-content: center;
|
|
1750
|
+
padding: 0;
|
|
1751
|
+
border: none;
|
|
1752
|
+
border-radius: 4px;
|
|
1753
|
+
background: transparent;
|
|
1754
|
+
color: hsl(var(--tn-comment));
|
|
1755
|
+
cursor: pointer;
|
|
1756
|
+
transition: all 0.15s;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
.fw-sc-layer-visibility:hover {
|
|
1760
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
.fw-sc-layer-visibility--visible {
|
|
1764
|
+
color: hsl(var(--tn-fg));
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
.fw-sc-layer-icon {
|
|
1768
|
+
font-size: 1rem;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
.fw-sc-layer-name {
|
|
1772
|
+
flex: 1;
|
|
1773
|
+
font-size: 0.875rem;
|
|
1774
|
+
overflow: hidden;
|
|
1775
|
+
text-overflow: ellipsis;
|
|
1776
|
+
white-space: nowrap;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
.fw-sc-layer-opacity {
|
|
1780
|
+
display: flex;
|
|
1781
|
+
align-items: center;
|
|
1782
|
+
gap: 0.5rem;
|
|
1783
|
+
margin-right: 0.5rem;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
.fw-sc-layer-opacity input[type="range"] {
|
|
1787
|
+
width: 60px;
|
|
1788
|
+
height: 4px;
|
|
1789
|
+
-webkit-appearance: none;
|
|
1790
|
+
appearance: none;
|
|
1791
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1792
|
+
border-radius: 2px;
|
|
1793
|
+
cursor: pointer;
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
.fw-sc-layer-opacity input[type="range"]::-webkit-slider-thumb {
|
|
1797
|
+
-webkit-appearance: none;
|
|
1798
|
+
width: 12px;
|
|
1799
|
+
height: 12px;
|
|
1800
|
+
border-radius: 50%;
|
|
1801
|
+
background: hsl(var(--tn-fg));
|
|
1802
|
+
cursor: pointer;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
.fw-sc-layer-opacity input[type="range"]::-moz-range-thumb {
|
|
1806
|
+
width: 12px;
|
|
1807
|
+
height: 12px;
|
|
1808
|
+
border-radius: 50%;
|
|
1809
|
+
background: hsl(var(--tn-fg));
|
|
1810
|
+
border: none;
|
|
1811
|
+
cursor: pointer;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
.fw-sc-layer-opacity span {
|
|
1815
|
+
font-size: 0.7rem;
|
|
1816
|
+
color: hsl(var(--tn-fg-dark));
|
|
1817
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
1818
|
+
min-width: 32px;
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
.fw-sc-layer-controls {
|
|
1822
|
+
display: flex;
|
|
1823
|
+
align-items: center;
|
|
1824
|
+
gap: 0.125rem;
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
.fw-sc-layer-btn {
|
|
1828
|
+
width: 24px;
|
|
1829
|
+
height: 24px;
|
|
1830
|
+
display: flex;
|
|
1831
|
+
align-items: center;
|
|
1832
|
+
justify-content: center;
|
|
1833
|
+
padding: 0;
|
|
1834
|
+
border: none;
|
|
1835
|
+
border-radius: 4px;
|
|
1836
|
+
background: transparent;
|
|
1837
|
+
color: hsl(var(--tn-comment));
|
|
1838
|
+
font-size: 0.875rem;
|
|
1839
|
+
cursor: pointer;
|
|
1840
|
+
transition: all 0.15s;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
.fw-sc-layer-btn:hover:not(:disabled) {
|
|
1844
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1845
|
+
color: hsl(var(--tn-fg));
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
.fw-sc-layer-btn:disabled {
|
|
1849
|
+
opacity: 0.3;
|
|
1850
|
+
cursor: not-allowed;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
.fw-sc-layer-btn--active {
|
|
1854
|
+
background: hsl(var(--tn-blue) / 0.2);
|
|
1855
|
+
color: hsl(var(--tn-blue));
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
.fw-sc-layer-btn--danger:hover:not(:disabled) {
|
|
1859
|
+
background: hsl(var(--tn-red) / 0.2);
|
|
1860
|
+
color: hsl(var(--tn-red));
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
/* ============================================================================
|
|
1864
|
+
* Compact Layout Overlay
|
|
1865
|
+
* ============================================================================ */
|
|
1866
|
+
|
|
1867
|
+
.fw-sc-layout-overlay {
|
|
1868
|
+
position: absolute;
|
|
1869
|
+
bottom: 8px;
|
|
1870
|
+
left: 50%;
|
|
1871
|
+
transform: translateX(-50%);
|
|
1872
|
+
z-index: 20;
|
|
1873
|
+
display: flex;
|
|
1874
|
+
flex-direction: column;
|
|
1875
|
+
align-items: center;
|
|
1876
|
+
gap: 4px;
|
|
1877
|
+
opacity: 0.7;
|
|
1878
|
+
transition: opacity 0.2s ease;
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
.fw-sc-layout-overlay:hover,
|
|
1882
|
+
.fw-sc-layout-overlay--expanded {
|
|
1883
|
+
opacity: 1;
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
.fw-sc-layout-bar {
|
|
1887
|
+
display: flex;
|
|
1888
|
+
align-items: center;
|
|
1889
|
+
gap: 2px;
|
|
1890
|
+
padding: 4px 6px;
|
|
1891
|
+
background: hsl(var(--tn-bg-dark) / 0.9);
|
|
1892
|
+
backdrop-filter: blur(8px);
|
|
1893
|
+
border-radius: 6px;
|
|
1894
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
1895
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
.fw-sc-layout-icons,
|
|
1899
|
+
.fw-sc-scaling-icons {
|
|
1900
|
+
display: flex;
|
|
1901
|
+
align-items: center;
|
|
1902
|
+
gap: 1px;
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
.fw-sc-layout-icon {
|
|
1906
|
+
width: 22px;
|
|
1907
|
+
height: 22px;
|
|
1908
|
+
display: flex;
|
|
1909
|
+
align-items: center;
|
|
1910
|
+
justify-content: center;
|
|
1911
|
+
padding: 0;
|
|
1912
|
+
border: none;
|
|
1913
|
+
border-radius: 4px;
|
|
1914
|
+
background: transparent;
|
|
1915
|
+
color: hsl(var(--tn-fg-dark));
|
|
1916
|
+
cursor: pointer;
|
|
1917
|
+
transition: all 0.15s ease;
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
.fw-sc-layout-icon:hover {
|
|
1921
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1922
|
+
color: hsl(var(--tn-fg));
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
.fw-sc-layout-icon--active {
|
|
1926
|
+
background: hsl(var(--tn-blue) / 0.2);
|
|
1927
|
+
color: hsl(var(--tn-blue));
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
.fw-sc-layout-icon--active:hover {
|
|
1931
|
+
background: hsl(var(--tn-blue) / 0.3);
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
.fw-sc-layout-separator {
|
|
1935
|
+
width: 1px;
|
|
1936
|
+
height: 14px;
|
|
1937
|
+
background: hsl(var(--tn-fg-gutter) / 0.5);
|
|
1938
|
+
margin: 0 4px;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
.fw-sc-layout-stats {
|
|
1942
|
+
font-size: 0.65rem;
|
|
1943
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
1944
|
+
color: hsl(var(--tn-fg-dark));
|
|
1945
|
+
white-space: nowrap;
|
|
1946
|
+
padding: 0 2px;
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
/* Source chips (expanded on hover) */
|
|
1950
|
+
.fw-sc-layout-sources {
|
|
1951
|
+
display: flex;
|
|
1952
|
+
align-items: center;
|
|
1953
|
+
gap: 4px;
|
|
1954
|
+
padding: 4px 6px;
|
|
1955
|
+
background: hsl(var(--tn-bg-dark) / 0.9);
|
|
1956
|
+
backdrop-filter: blur(8px);
|
|
1957
|
+
border-radius: 6px;
|
|
1958
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
1959
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
.fw-sc-source-chip {
|
|
1963
|
+
display: flex;
|
|
1964
|
+
align-items: center;
|
|
1965
|
+
gap: 4px;
|
|
1966
|
+
padding: 3px 8px;
|
|
1967
|
+
border: none;
|
|
1968
|
+
border-radius: 4px;
|
|
1969
|
+
background: hsl(var(--tn-bg-highlight));
|
|
1970
|
+
color: hsl(var(--tn-fg));
|
|
1971
|
+
font-size: 0.7rem;
|
|
1972
|
+
cursor: pointer;
|
|
1973
|
+
transition: all 0.15s ease;
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
.fw-sc-source-chip:hover {
|
|
1977
|
+
background: hsl(var(--tn-bg-highlight) / 1.5);
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
.fw-sc-source-chip--hidden {
|
|
1981
|
+
background: hsl(var(--tn-bg-dark));
|
|
1982
|
+
color: hsl(var(--tn-comment));
|
|
1983
|
+
opacity: 0.6;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
.fw-sc-source-chip--hidden:hover {
|
|
1987
|
+
opacity: 0.9;
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
.fw-sc-source-chip-icon {
|
|
1991
|
+
font-size: 0.75rem;
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
.fw-sc-source-chip-label {
|
|
1995
|
+
max-width: 80px;
|
|
1996
|
+
overflow: hidden;
|
|
1997
|
+
text-overflow: ellipsis;
|
|
1998
|
+
white-space: nowrap;
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
/* ============================================================================
|
|
2002
|
+
* Layout Bar Sections & Labels
|
|
2003
|
+
* ============================================================================ */
|
|
2004
|
+
|
|
2005
|
+
.fw-sc-layout-section {
|
|
2006
|
+
display: flex;
|
|
2007
|
+
align-items: center;
|
|
2008
|
+
gap: 4px;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
.fw-sc-layout-label {
|
|
2012
|
+
font-size: 0.6rem;
|
|
2013
|
+
font-weight: 500;
|
|
2014
|
+
text-transform: uppercase;
|
|
2015
|
+
letter-spacing: 0.05em;
|
|
2016
|
+
color: hsl(var(--tn-comment));
|
|
2017
|
+
padding: 0 4px;
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
/* ============================================================================
|
|
2021
|
+
* Custom Instant Tooltips
|
|
2022
|
+
* ============================================================================ */
|
|
2023
|
+
|
|
2024
|
+
.fw-sc-tooltip-wrapper {
|
|
2025
|
+
position: relative;
|
|
2026
|
+
display: inline-flex;
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
.fw-sc-tooltip {
|
|
2030
|
+
position: absolute;
|
|
2031
|
+
bottom: calc(100% + 8px);
|
|
2032
|
+
left: 50%;
|
|
2033
|
+
transform: translateX(-50%);
|
|
2034
|
+
padding: 4px 8px;
|
|
2035
|
+
background: hsl(var(--tn-bg-dark));
|
|
2036
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.5);
|
|
2037
|
+
border-radius: 4px;
|
|
2038
|
+
font-size: 0.7rem;
|
|
2039
|
+
font-weight: 500;
|
|
2040
|
+
color: hsl(var(--tn-fg));
|
|
2041
|
+
white-space: nowrap;
|
|
2042
|
+
z-index: 100;
|
|
2043
|
+
pointer-events: none;
|
|
2044
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
.fw-sc-tooltip::after {
|
|
2048
|
+
content: "";
|
|
2049
|
+
position: absolute;
|
|
2050
|
+
top: 100%;
|
|
2051
|
+
left: 50%;
|
|
2052
|
+
transform: translateX(-50%);
|
|
2053
|
+
border: 5px solid transparent;
|
|
2054
|
+
border-top-color: hsl(var(--tn-fg-gutter) / 0.5);
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
.fw-sc-tooltip::before {
|
|
2058
|
+
content: "";
|
|
2059
|
+
position: absolute;
|
|
2060
|
+
top: 100%;
|
|
2061
|
+
left: 50%;
|
|
2062
|
+
transform: translateX(-50%);
|
|
2063
|
+
border: 4px solid transparent;
|
|
2064
|
+
border-top-color: hsl(var(--tn-bg-dark));
|
|
2065
|
+
margin-top: -1px;
|
|
2066
|
+
}
|
|
2067
|
+
} /* End @layer fw-streamcrafter */
|
|
2068
|
+
|
|
2069
|
+
`,xe=c`
|
|
2070
|
+
.flex {
|
|
2071
|
+
display: flex;
|
|
2072
|
+
}
|
|
2073
|
+
.inline-flex {
|
|
2074
|
+
display: inline-flex;
|
|
2075
|
+
}
|
|
2076
|
+
.block {
|
|
2077
|
+
display: block;
|
|
2078
|
+
}
|
|
2079
|
+
.hidden {
|
|
2080
|
+
display: none;
|
|
2081
|
+
}
|
|
2082
|
+
.contents {
|
|
2083
|
+
display: contents;
|
|
2084
|
+
}
|
|
2085
|
+
.flex-col {
|
|
2086
|
+
flex-direction: column;
|
|
2087
|
+
}
|
|
2088
|
+
.flex-row {
|
|
2089
|
+
flex-direction: row;
|
|
2090
|
+
}
|
|
2091
|
+
.flex-1 {
|
|
2092
|
+
flex: 1 1 0%;
|
|
2093
|
+
}
|
|
2094
|
+
.flex-none {
|
|
2095
|
+
flex: none;
|
|
2096
|
+
}
|
|
2097
|
+
.flex-wrap {
|
|
2098
|
+
flex-wrap: wrap;
|
|
2099
|
+
}
|
|
2100
|
+
.items-center {
|
|
2101
|
+
align-items: center;
|
|
2102
|
+
}
|
|
2103
|
+
.items-start {
|
|
2104
|
+
align-items: flex-start;
|
|
2105
|
+
}
|
|
2106
|
+
.justify-center {
|
|
2107
|
+
justify-content: center;
|
|
2108
|
+
}
|
|
2109
|
+
.justify-between {
|
|
2110
|
+
justify-content: space-between;
|
|
2111
|
+
}
|
|
2112
|
+
.gap-0\\.5 {
|
|
2113
|
+
gap: 0.125rem;
|
|
2114
|
+
}
|
|
2115
|
+
.gap-1 {
|
|
2116
|
+
gap: 0.25rem;
|
|
2117
|
+
}
|
|
2118
|
+
.gap-2 {
|
|
2119
|
+
gap: 0.5rem;
|
|
2120
|
+
}
|
|
2121
|
+
.gap-3 {
|
|
2122
|
+
gap: 0.75rem;
|
|
2123
|
+
}
|
|
2124
|
+
.gap-4 {
|
|
2125
|
+
gap: 1rem;
|
|
2126
|
+
}
|
|
2127
|
+
.w-full {
|
|
2128
|
+
width: 100%;
|
|
2129
|
+
}
|
|
2130
|
+
.h-full {
|
|
2131
|
+
height: 100%;
|
|
2132
|
+
}
|
|
2133
|
+
.min-w-0 {
|
|
2134
|
+
min-width: 0;
|
|
2135
|
+
}
|
|
2136
|
+
.relative {
|
|
2137
|
+
position: relative;
|
|
2138
|
+
}
|
|
2139
|
+
.absolute {
|
|
2140
|
+
position: absolute;
|
|
2141
|
+
}
|
|
2142
|
+
.inset-0 {
|
|
2143
|
+
inset: 0;
|
|
2144
|
+
}
|
|
2145
|
+
.overflow-hidden {
|
|
2146
|
+
overflow: hidden;
|
|
2147
|
+
}
|
|
2148
|
+
.overflow-auto {
|
|
2149
|
+
overflow: auto;
|
|
2150
|
+
}
|
|
2151
|
+
.text-xs {
|
|
2152
|
+
font-size: 0.75rem;
|
|
2153
|
+
line-height: 1rem;
|
|
2154
|
+
}
|
|
2155
|
+
.text-sm {
|
|
2156
|
+
font-size: 0.875rem;
|
|
2157
|
+
line-height: 1.25rem;
|
|
2158
|
+
}
|
|
2159
|
+
.font-mono {
|
|
2160
|
+
font-family:
|
|
2161
|
+
ui-monospace,
|
|
2162
|
+
SFMono-Regular,
|
|
2163
|
+
SF Mono,
|
|
2164
|
+
Menlo,
|
|
2165
|
+
Consolas,
|
|
2166
|
+
monospace;
|
|
2167
|
+
}
|
|
2168
|
+
.font-medium {
|
|
2169
|
+
font-weight: 500;
|
|
2170
|
+
}
|
|
2171
|
+
.font-semibold {
|
|
2172
|
+
font-weight: 600;
|
|
2173
|
+
}
|
|
2174
|
+
.uppercase {
|
|
2175
|
+
text-transform: uppercase;
|
|
2176
|
+
}
|
|
2177
|
+
.tracking-wider {
|
|
2178
|
+
letter-spacing: 0.05em;
|
|
2179
|
+
}
|
|
2180
|
+
.whitespace-nowrap {
|
|
2181
|
+
white-space: nowrap;
|
|
2182
|
+
}
|
|
2183
|
+
.tabular-nums {
|
|
2184
|
+
font-variant-numeric: tabular-nums;
|
|
2185
|
+
}
|
|
2186
|
+
.rounded {
|
|
2187
|
+
border-radius: 0.25rem;
|
|
2188
|
+
}
|
|
2189
|
+
.rounded-md {
|
|
2190
|
+
border-radius: 0.375rem;
|
|
2191
|
+
}
|
|
2192
|
+
.rounded-lg {
|
|
2193
|
+
border-radius: 0.5rem;
|
|
2194
|
+
}
|
|
2195
|
+
.border-none {
|
|
2196
|
+
border: none;
|
|
2197
|
+
}
|
|
2198
|
+
.cursor-pointer {
|
|
2199
|
+
cursor: pointer;
|
|
2200
|
+
}
|
|
2201
|
+
.cursor-not-allowed {
|
|
2202
|
+
cursor: not-allowed;
|
|
2203
|
+
}
|
|
2204
|
+
.pointer-events-none {
|
|
2205
|
+
pointer-events: none;
|
|
2206
|
+
}
|
|
2207
|
+
.select-none {
|
|
2208
|
+
user-select: none;
|
|
2209
|
+
}
|
|
2210
|
+
.opacity-50 {
|
|
2211
|
+
opacity: 0.5;
|
|
2212
|
+
}
|
|
2213
|
+
.opacity-70 {
|
|
2214
|
+
opacity: 0.7;
|
|
2215
|
+
}
|
|
2216
|
+
.transition {
|
|
2217
|
+
transition: all 150ms ease;
|
|
2218
|
+
}
|
|
2219
|
+
.p-0 {
|
|
2220
|
+
padding: 0;
|
|
2221
|
+
}
|
|
2222
|
+
.p-2 {
|
|
2223
|
+
padding: 0.5rem;
|
|
2224
|
+
}
|
|
2225
|
+
.px-2 {
|
|
2226
|
+
padding-left: 0.5rem;
|
|
2227
|
+
padding-right: 0.5rem;
|
|
2228
|
+
}
|
|
2229
|
+
.py-1 {
|
|
2230
|
+
padding-top: 0.25rem;
|
|
2231
|
+
padding-bottom: 0.25rem;
|
|
2232
|
+
}
|
|
2233
|
+
.m-0 {
|
|
2234
|
+
margin: 0;
|
|
2235
|
+
}
|
|
2236
|
+
.bg-none {
|
|
2237
|
+
background: none;
|
|
2238
|
+
}
|
|
2239
|
+
`,Se=(e=18)=>q` <svg
|
|
2240
|
+
width="${e}"
|
|
2241
|
+
height="${e}"
|
|
2242
|
+
viewBox="0 0 24 24"
|
|
2243
|
+
fill="none"
|
|
2244
|
+
stroke="currentColor"
|
|
2245
|
+
stroke-width="2"
|
|
2246
|
+
stroke-linecap="round"
|
|
2247
|
+
stroke-linejoin="round"
|
|
2248
|
+
>
|
|
2249
|
+
<path d="M23 7l-7 5 7 5V7z" />
|
|
2250
|
+
<rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
|
|
2251
|
+
</svg>`,ke=(e=18)=>q` <svg
|
|
2252
|
+
width="${e}"
|
|
2253
|
+
height="${e}"
|
|
2254
|
+
viewBox="0 0 24 24"
|
|
2255
|
+
fill="none"
|
|
2256
|
+
stroke="currentColor"
|
|
2257
|
+
stroke-width="2"
|
|
2258
|
+
stroke-linecap="round"
|
|
2259
|
+
stroke-linejoin="round"
|
|
2260
|
+
>
|
|
2261
|
+
<rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
|
|
2262
|
+
<line x1="8" y1="21" x2="16" y2="21" />
|
|
2263
|
+
<line x1="12" y1="17" x2="12" y2="21" />
|
|
2264
|
+
</svg>`,Ce=(e=14)=>q` <svg
|
|
2265
|
+
width="${e}"
|
|
2266
|
+
height="${e}"
|
|
2267
|
+
viewBox="0 0 24 24"
|
|
2268
|
+
fill="none"
|
|
2269
|
+
stroke="currentColor"
|
|
2270
|
+
stroke-width="2"
|
|
2271
|
+
stroke-linecap="round"
|
|
2272
|
+
stroke-linejoin="round"
|
|
2273
|
+
>
|
|
2274
|
+
<line x1="18" y1="6" x2="6" y2="18" />
|
|
2275
|
+
<line x1="6" y1="6" x2="18" y2="18" />
|
|
2276
|
+
</svg>`,$e=(e=14)=>q` <svg
|
|
2277
|
+
width="${e}"
|
|
2278
|
+
height="${e}"
|
|
2279
|
+
viewBox="0 0 24 24"
|
|
2280
|
+
fill="none"
|
|
2281
|
+
stroke="currentColor"
|
|
2282
|
+
stroke-width="2"
|
|
2283
|
+
stroke-linecap="round"
|
|
2284
|
+
stroke-linejoin="round"
|
|
2285
|
+
>
|
|
2286
|
+
<polygon points="23 7 16 12 23 17 23 7" />
|
|
2287
|
+
<rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
|
|
2288
|
+
</svg>`,Me={enabled:!1,width:1920,height:1080,frameRate:30,renderer:"auto",defaultTransition:{type:"fade",durationMs:500,easing:"ease-in-out"}},Te={x:0,y:0,width:1,height:1,opacity:1,rotation:0,borderRadius:0,crop:{top:0,right:0,bottom:0,left:0}};class Ae{constructor(){this.listeners=new Map}on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.off(e,t)}once(e,t){const r=i=>{this.off(e,r),t(i)};return this.on(e,r)}off(e,t){const r=this.listeners.get(e);r&&(r.delete(t),0===r.size&&this.listeners.delete(e))}emit(e,t){const r=this.listeners.get(e);r&&r.forEach(r=>{try{r(t)}catch(t){console.error(`Error in event handler for "${String(e)}":`,t)}})}removeAllListeners(e){e?this.listeners.delete(e):this.listeners.clear()}listenerCount(e){return this.listeners.get(e)?.size??0}}function _e(e="professional"){const t={professional:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30}},broadcast:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30}},conference:{width:{ideal:1280},height:{ideal:720},frameRate:{ideal:24}},auto:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30}}};return t[e]||t.professional}function Ee(e,t){const r=function(e="professional"){const t={professional:{echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1,sampleRate:48e3,channelCount:2,latency:.01},broadcast:{echoCancellation:!1,noiseSuppression:!0,autoGainControl:!1,sampleRate:48e3,channelCount:2,latency:.02},conference:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:44100,channelCount:1,latency:.05},auto:{echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1,sampleRate:48e3,channelCount:2}};return t[e]||t.professional}(e),i=_e(e);return{audio:{echoCancellation:r.echoCancellation,noiseSuppression:r.noiseSuppression,autoGainControl:r.autoGainControl,sampleRate:r.sampleRate,channelCount:r.channelCount,...t?.audioDeviceId&&{deviceId:{exact:t.audioDeviceId}}},video:{width:i.width,height:i.height,frameRate:i.frameRate,...t?.videoDeviceId&&{deviceId:{exact:t.videoDeviceId}},...t?.facingMode&&{facingMode:t.facingMode}}}}class Ie extends Ae{constructor(){super(),this.devices=[],this.currentStream=null,this.permissionStatus={video:!1,audio:!1},this.setupDeviceChangeListener()}setupDeviceChangeListener(){"undefined"!=typeof navigator&&navigator.mediaDevices&&navigator.mediaDevices.addEventListener("devicechange",async()=>{await this.enumerateDevices(),this.emit("devicesChanged",{devices:this.devices})})}async enumerateDevices(){if(!navigator.mediaDevices?.enumerateDevices)throw new Error("enumerateDevices not supported");const e=await navigator.mediaDevices.enumerateDevices();return this.devices=e.filter(e=>"audioinput"===e.kind||"videoinput"===e.kind||"audiooutput"===e.kind).map(e=>({deviceId:e.deviceId,kind:e.kind,label:e.label||`${e.kind} (${e.deviceId.slice(0,8)}...)`,groupId:e.groupId})),this.devices}async getVideoInputs(){return await this.enumerateDevices(),this.devices.filter(e=>"videoinput"===e.kind)}async getAudioInputs(){return await this.enumerateDevices(),this.devices.filter(e=>"audioinput"===e.kind)}async getAudioOutputs(){return await this.enumerateDevices(),this.devices.filter(e=>"audiooutput"===e.kind)}async requestPermissions(e={video:!0,audio:!0}){try{return(await navigator.mediaDevices.getUserMedia({video:e.video,audio:e.audio})).getTracks().forEach(e=>e.stop()),e.video&&(this.permissionStatus.video=!0),e.audio&&(this.permissionStatus.audio=!0),await this.enumerateDevices(),this.emit("permissionChanged",{granted:!0,denied:!1}),this.permissionStatus}catch(e){const t=e instanceof Error?e:new Error(String(e));throw"NotAllowedError"!==t.name&&"PermissionDeniedError"!==t.name||this.emit("permissionChanged",{granted:!1,denied:!0}),this.emit("error",{message:`Permission request failed: ${t.message}`,error:t}),e}}hasPermission(e){return this.permissionStatus[e]}async getUserMedia(e={}){const t=e.profile||"professional";let r;r=e.customConstraints?e.customConstraints:Ee(t,{videoDeviceId:e.videoDeviceId,audioDeviceId:e.audioDeviceId,facingMode:e.facingMode});try{const e=await navigator.mediaDevices.getUserMedia(r);return this.currentStream=e,e.getVideoTracks().length>0&&(this.permissionStatus.video=!0),e.getAudioTracks().length>0&&(this.permissionStatus.audio=!0),e}catch(e){const t=e instanceof Error?e:new Error(String(e));if("OverconstrainedError"===t.name){const e={video:!!r.video,audio:!!r.audio},t=await navigator.mediaDevices.getUserMedia(e);return this.currentStream=t,t}throw this.emit("error",{message:`getUserMedia failed: ${t.message}`,error:t}),e}}getStream(){return this.currentStream}stopAllTracks(){this.currentStream&&(this.currentStream.getTracks().forEach(e=>{e.stop()}),this.currentStream=null)}async replaceVideoTrack(e,t="professional"){if(!this.currentStream)throw new Error("No active stream to replace track in");const r=this.currentStream.getVideoTracks()[0];r&&(r.stop(),this.currentStream.removeTrack(r));const i=Ee(t,{videoDeviceId:e}),s=(await navigator.mediaDevices.getUserMedia({video:i.video,audio:!1})).getVideoTracks()[0];return s&&this.currentStream.addTrack(s),s||null}async replaceAudioTrack(e,t="professional"){if(!this.currentStream)throw new Error("No active stream to replace track in");const r=this.currentStream.getAudioTracks()[0];r&&(r.stop(),this.currentStream.removeTrack(r));const i=Ee(t,{audioDeviceId:e}),s=(await navigator.mediaDevices.getUserMedia({video:!1,audio:i.audio})).getAudioTracks()[0];return s&&this.currentStream.addTrack(s),s||null}getAllDevices(){return[...this.devices]}destroy(){this.stopAllTracks(),this.removeAllListeners()}}class Pe extends Ae{constructor(){super(...arguments),this.captures=new Map,this.captureCounter=0}async start(e={}){try{let t;if(!1===e.video)t=!1;else if("object"==typeof e.video){t={frameRate:{ideal:30,max:60},width:{ideal:1920},height:{ideal:1080},...e.video,...void 0!==e.cursor?{cursor:e.cursor}:{}}}else t={frameRate:{ideal:30,max:60},width:{ideal:1920},height:{ideal:1080},...void 0!==e.cursor?{cursor:e.cursor}:{}};const r={video:t,audio:e.audio??!1};e.preferCurrentTab&&"preferCurrentTab"in r&&(r.preferCurrentTab=!0),e.surfaceSwitching&&(r.surfaceSwitching="include"),void 0!==e.selfBrowserSurface&&(r.selfBrowserSurface=e.selfBrowserSurface),e.monitorTypeSurfaces&&(r.monitorTypeSurfaces=e.monitorTypeSurfaces),e.systemAudio&&r.audio&&"object"==typeof r.audio&&(r.audio.systemAudio=e.systemAudio);const i=await navigator.mediaDevices.getDisplayMedia(r),s=`screen-${++this.captureCounter}-${Date.now()}`,o=i.getVideoTracks()[0],n=o?.label||`Screen ${this.captureCounter}`;return this.captures.set(s,{stream:i,label:n}),i.getTracks().forEach(e=>{e.addEventListener("ended",()=>{this.handleTrackEnded(s,i)})}),this.emit("started",{stream:i,captureId:s}),i}catch(e){const t=e instanceof Error?e:new Error(String(e));if("AbortError"===t.name||"NotAllowedError"===t.name)return this.emit("ended",{captureId:"",stream:null,reason:"cancelled"}),null;throw this.emit("error",{message:`Screen capture failed: ${t.message}`,error:t}),e}}handleTrackEnded(e,t){const r=t.getTracks().filter(e=>"live"===e.readyState);0===r.length&&(this.captures.delete(e),this.emit("ended",{captureId:e,stream:t,reason:"user_stopped"}))}stopByStream(e){for(const[t,r]of this.captures)if(r.stream===e)return r.stream.getTracks().forEach(e=>e.stop()),this.captures.delete(t),void this.emit("ended",{captureId:t,stream:e,reason:"stopped"})}stop(){for(const[e,t]of this.captures)t.stream.getTracks().forEach(e=>e.stop()),this.emit("ended",{captureId:e,stream:t.stream,reason:"stopped"});this.captures.clear()}getCaptures(){return Array.from(this.captures.entries()).map(([e,t])=>({captureId:e,stream:t.stream,label:t.label}))}isActive(){return this.captures.size>0}getCaptureCount(){return this.captures.size}getStream(){const e=this.captures.values().next().value;return e?.stream??null}getVideoTrack(){const e=this.getStream();return e?.getVideoTracks()[0]??null}getAudioTrack(){const e=this.getStream();return e?.getAudioTracks()[0]??null}hasAudio(){for(const[,e]of this.captures)if(e.stream.getAudioTracks().length>0)return!0;return!1}destroy(){this.stop(),this.removeAllListeners()}}class Re extends Ae{constructor(e){super(),this.peerConnection=null,this.videoTrackGenerator=null,this.audioTrackGenerator=null,this.state="disconnected",this.videoWriter=null,this.audioWriter=null,this.videoWriteQueue=[],this.audioWriteQueue=[],this.isProcessingVideoQueue=!1,this.isProcessingAudioQueue=!1,this.videoTransformWorker=null,this.audioTransformWorker=null,this.encoderListenerCleanup=null,this.negotiatedVideoCodec=null,this.negotiatedAudioCodec=null,this.resourceUrl=null,this.config=e}log(e,t){this.config.debug&&console.log(`[WHIP] ${e}`,t??"")}logError(e,t){console.error(`[WHIP ERROR] ${e}`,t??""),this.emit("error",{message:e,error:t})}setState(e){const t=this.state;this.state=e,this.emit("stateChange",{state:e,previousState:t})}preferCodecs(e){const t=e.getTransceivers();for(const e of t){if(!e.setCodecPreferences)continue;const t=e.sender.track?.kind;if("video"===t){const t=RTCRtpSender.getCapabilities("video");if(!t?.codecs)continue;const r=t.codecs.filter(e=>"video/VP9"===e.mimeType||"video/H264"===e.mimeType).sort((e,t)=>"video/VP9"===e.mimeType&&"video/VP9"!==t.mimeType?-1:"video/VP9"!==e.mimeType&&"video/VP9"===t.mimeType?1:0);if(r.length>0)try{e.setCodecPreferences(r),this.log("Set video codec preferences",r.map(e=>e.mimeType))}catch(e){this.log("Failed to set video codec preferences",e)}}if("audio"===t){const t=RTCRtpSender.getCapabilities("audio");if(!t?.codecs)continue;const r=t.codecs.filter(e=>"audio/opus"===e.mimeType);if(r.length>0)try{e.setCodecPreferences(r),this.log("Set audio codec preferences",r.map(e=>e.mimeType))}catch(e){this.log("Failed to set audio codec preferences",e)}}}}verifyCodecAlignment(){if(!this.peerConnection)return;const e=this.peerConnection.getSenders();for(const t of e){const e=t.getParameters(),r=e.codecs?.[0];"video"===t.track?.kind&&r?.mimeType&&(this.negotiatedVideoCodec=r.mimeType,this.log("Negotiated video codec",r.mimeType)),"audio"===t.track?.kind&&r?.mimeType&&(this.negotiatedAudioCodec=r.mimeType,this.log("Negotiated audio codec",r.mimeType))}}canUseEncodedInsertion(){if(!this.peerConnection||"connected"!==this.state)return this.log("canUseEncodedInsertion: no connection",{hasPC:!!this.peerConnection,state:this.state}),!1;if("undefined"==typeof RTCRtpScriptTransform)return this.log("canUseEncodedInsertion: RTCRtpScriptTransform not supported"),!1;const e=this.peerConnection.getSenders().some(e=>"transform"in e);if(!e)return this.log("Sender transform not supported"),!1;if(this.negotiatedVideoCodec){if(!("video/VP9"===this.negotiatedVideoCodec||"video/H264"===this.negotiatedVideoCodec))return this.log("Video codec not compatible with WebCodecs",this.negotiatedVideoCodec),!1}if(this.negotiatedAudioCodec){if(!("audio/opus"===this.negotiatedAudioCodec))return this.log("Audio codec not compatible with WebCodecs",this.negotiatedAudioCodec),!1}return!0}getNegotiatedVideoCodec(){return this.negotiatedVideoCodec}getNegotiatedAudioCodec(){return this.negotiatedAudioCodec}get isConnected(){return"connected"===this.state}async connect(e){try{if(this.log("Starting WHIP connection"),this.setState("connecting"),!this.config.whipUrl)throw new Error("WHIP URL is required");const t={iceServers:this.config.iceServers||[]};this.log("Creating RTCPeerConnection",t);const r=new RTCPeerConnection(t);r.onconnectionstatechange=()=>{const e=r.connectionState;switch(this.log(`Connection state changed: ${e}`),e){case"connected":this.log("WHIP streaming connected successfully"),this.setState("connected");break;case"disconnected":this.setState("disconnected");break;case"failed":this.setState("failed");break;case"closed":this.setState("closed")}},r.oniceconnectionstatechange=()=>{this.log(`ICE connection state: ${r.iceConnectionState}`)},r.onicegatheringstatechange=()=>{this.log(`ICE gathering state: ${r.iceGatheringState}`)},r.onicecandidate=e=>{this.emit("iceCandidate",{candidate:e.candidate}),e.candidate?this.log("ICE candidate generated",e.candidate.candidate):this.log("ICE candidate gathering complete")},this.log("Adding tracks to peer connection"),e.getTracks().forEach((t,i)=>{this.log(`Adding ${t.kind} track ${i}`,{id:t.id,kind:t.kind,enabled:t.enabled,readyState:t.readyState}),r.addTrack(t,e)}),this.peerConnection=r,this.preferCodecs(r),this.log("Creating offer");const i=await r.createOffer({offerToReceiveAudio:!1,offerToReceiveVideo:!1});this.log("Setting local description"),await r.setLocalDescription(i),this.log("Local SDP offer created",{type:i.type,sdpLength:i.sdp?.length}),this.log(`Sending offer to WHIP endpoint: ${this.config.whipUrl}`);const s=await fetch(this.config.whipUrl,{method:"POST",headers:{"Content-Type":"application/sdp",Accept:"application/sdp"},body:i.sdp});if(this.log(`WHIP response status: ${s.status} ${s.statusText}`),!s.ok){const e=await s.text().catch(()=>"Unknown error");throw new Error(`WHIP request failed: ${s.status} ${s.statusText} - ${e}`)}this.resourceUrl=s.headers.get("Location"),this.resourceUrl&&this.log("WHIP resource URL:",this.resourceUrl);const o=await s.text();this.log("Received SDP answer",{length:o.length}),await r.setRemoteDescription({type:"answer",sdp:o}),this.log("Remote description set successfully"),this.verifyCodecAlignment(),this.log("WHIP connection established, waiting for ICE connection...")}catch(e){throw this.logError("Failed to connect",e instanceof Error?e:new Error(String(e))),this.setState("failed"),this.cleanup(),e}}async connectWithGenerators(){try{if(this.log("Starting WHIP connection with track generators"),this.setState("connecting"),!this.config.whipUrl)throw new Error("WHIP URL is required");this.log("Creating MediaStreamTrackGenerators");const e=new MediaStreamTrackGenerator({kind:"video"}),t=new MediaStreamTrackGenerator({kind:"audio"});this.videoTrackGenerator=e,this.audioTrackGenerator=t,this.log("Track generators created successfully");const r={iceServers:this.config.iceServers||[]};this.log("Creating RTCPeerConnection",r);const i=new RTCPeerConnection(r);i.onconnectionstatechange=()=>{const e=i.connectionState;switch(this.log(`Connection state changed: ${e}`),e){case"connected":this.log("WHIP streaming connected successfully"),this.setState("connected");break;case"disconnected":this.setState("disconnected");break;case"failed":this.setState("failed");break;case"closed":this.setState("closed")}},i.oniceconnectionstatechange=()=>{this.log(`ICE connection state: ${i.iceConnectionState}`)},i.onicegatheringstatechange=()=>{this.log(`ICE gathering state: ${i.iceGatheringState}`)},i.onicecandidate=e=>{this.emit("iceCandidate",{candidate:e.candidate}),e.candidate?this.log("ICE candidate generated",e.candidate.candidate):this.log("ICE candidate gathering complete")};const s=new MediaStream([e,t]);this.log("Adding tracks to peer connection"),s.getTracks().forEach((e,t)=>{this.log(`Adding ${e.kind} track ${t}`,{id:e.id,kind:e.kind,enabled:e.enabled,readyState:e.readyState}),i.addTrack(e,s)}),this.peerConnection=i,this.preferCodecs(i),this.log("Creating offer");const o=await i.createOffer({offerToReceiveAudio:!1,offerToReceiveVideo:!1});this.log("Setting local description"),await i.setLocalDescription(o),this.log(`Sending offer to WHIP endpoint: ${this.config.whipUrl}`);const n=await fetch(this.config.whipUrl,{method:"POST",headers:{"Content-Type":"application/sdp",Accept:"application/sdp"},body:o.sdp});if(this.log(`WHIP response status: ${n.status} ${n.statusText}`),!n.ok){const e=await n.text().catch(()=>"Unknown error");throw new Error(`WHIP request failed: ${n.status} ${n.statusText} - ${e}`)}this.resourceUrl=n.headers.get("Location"),this.resourceUrl&&this.log("WHIP resource URL:",this.resourceUrl);const a=await n.text();return this.log("Received SDP answer",{length:a.length}),await i.setRemoteDescription({type:"answer",sdp:a}),this.log("Remote description set successfully"),this.verifyCodecAlignment(),this.log("WHIP connection established with generators"),{videoGenerator:e,audioGenerator:t}}catch(e){throw this.logError("Failed to connect with generators",e instanceof Error?e:new Error(String(e))),this.setState("failed"),this.cleanup(),e}}async processVideoQueue(){if(!this.isProcessingVideoQueue&&0!==this.videoWriteQueue.length){this.isProcessingVideoQueue=!0;try{for(;this.videoWriteQueue.length>0;){const e=this.videoWriteQueue.shift();if(!e)continue;const{frame:t,resolve:r,reject:i}=e;if(this.videoTrackGenerator)try{this.videoWriter||(this.videoWriter=this.videoTrackGenerator.writable.getWriter()),await this.videoWriter.write(t),r(!0)}catch(e){if(this.videoWriter){try{this.videoWriter.releaseLock()}catch{}this.videoWriter=null}i(e instanceof Error?e:new Error(String(e)))}else i(new Error("Video track generator not available"))}}finally{this.isProcessingVideoQueue=!1}}}async processAudioQueue(){if(!this.isProcessingAudioQueue&&0!==this.audioWriteQueue.length){this.isProcessingAudioQueue=!0;try{for(;this.audioWriteQueue.length>0;){const e=this.audioWriteQueue.shift();if(!e)continue;const{audioData:t,resolve:r,reject:i}=e;if(this.audioTrackGenerator)try{this.audioWriter||(this.audioWriter=this.audioTrackGenerator.writable.getWriter()),await this.audioWriter.write(t),r(!0)}catch(e){if(this.audioWriter){try{this.audioWriter.releaseLock()}catch{}this.audioWriter=null}i(e instanceof Error?e:new Error(String(e)))}else i(new Error("Audio track generator not available"))}}finally{this.isProcessingAudioQueue=!1}}}async sendVideoFrame(e){if(!this.videoTrackGenerator){if(e)try{e.close()}catch{}return!1}return new Promise((t,r)=>{this.videoWriteQueue.push({frame:e,resolve:t,reject:r}),this.processVideoQueue()})}async sendAudioData(e){if(!this.audioTrackGenerator){if(e)try{e.close()}catch{}return!1}return new Promise((t,r)=>{this.audioWriteQueue.push({audioData:e,resolve:t,reject:r}),this.processAudioQueue()})}async replaceTrack(e,t){if(!this.peerConnection)throw new Error("No peer connection");const r=this.peerConnection.getSenders().find(t=>t.track?.kind===e.kind);if(!r)throw new Error(`No sender found for ${e.kind} track`);await r.replaceTrack(t),this.log(`Replaced ${e.kind} track`)}async addTrack(e,t){if(!this.peerConnection)throw new Error("No peer connection");this.peerConnection.addTrack(e,t||new MediaStream([e])),this.log(`Added ${e.kind} track`)}async getStats(){if(!this.peerConnection)return null;try{return await this.peerConnection.getStats()}catch(e){return this.logError("Failed to get connection stats",e instanceof Error?e:new Error(String(e))),null}}getState(){return this.state}getPeerConnection(){return this.peerConnection}attachEncoderTransform(e,r){if(!this.peerConnection)throw new Error("No peer connection - call connect() first");if("undefined"==typeof RTCRtpScriptTransform)return void this.log("RTCRtpScriptTransform not supported, skipping encoder transform");this.log("Attaching encoder transform");const i=()=>{if(r)return new Worker(r,{type:"module"});try{const e=new URL("../workers/rtcTransform.worker.js",t&&"SCRIPT"===t.tagName.toUpperCase()&&t.src||new URL("fw-streamcrafter.iife.js",document.baseURI).href);return new Worker(e,{type:"module"})}catch(e){this.log("Packaged worker URL failed, trying fallback paths",e)}const e=["/workers/rtcTransform.worker.js","./workers/rtcTransform.worker.js","/node_modules/@livepeer-frameworks/streamcrafter-core/dist/workers/rtcTransform.worker.js"];for(const t of e)try{return new Worker(t,{type:"module"})}catch{try{return new Worker(t)}catch{}}return null},s=this.peerConnection.getSenders(),o=s.find(e=>"video"===e.track?.kind),n=s.find(e=>"audio"===e.track?.kind);o&&"transform"in o&&(this.log("Creating video transform worker"),this.videoTransformWorker=i(),this.videoTransformWorker?(this.videoTransformWorker.postMessage({type:"configure",config:{debug:this.config.debug,maxQueueSize:30}}),o.transform=new RTCRtpScriptTransform(this.videoTransformWorker,{kind:"video"}),this.log("Video transform attached")):this.logError("Failed to create video transform worker")),n&&"transform"in n&&(this.log("Creating audio transform worker"),this.audioTransformWorker=i(),this.audioTransformWorker?(this.audioTransformWorker.postMessage({type:"configure",config:{debug:this.config.debug,maxQueueSize:50}}),n.transform=new RTCRtpScriptTransform(this.audioTransformWorker,{kind:"audio"}),this.log("Audio transform attached")):this.logError("Failed to create audio transform worker"));const a=e=>{this.videoTransformWorker&&this.videoTransformWorker.postMessage({type:"videoChunk",data:e},[e.data])},c=e=>{this.audioTransformWorker&&this.audioTransformWorker.postMessage({type:"audioChunk",data:e},[e.data])};e.on("videoChunk",a),e.on("audioChunk",c),this.encoderListenerCleanup=()=>{e.off("videoChunk",a),e.off("audioChunk",c)},this.log("Encoder transform attached successfully")}hasEncoderTransform(){return null!==this.videoTransformWorker||null!==this.audioTransformWorker}detachEncoderTransform(){this.encoderListenerCleanup&&(this.encoderListenerCleanup(),this.encoderListenerCleanup=null),this.videoTransformWorker&&(this.videoTransformWorker.postMessage({type:"stop"}),this.videoTransformWorker.terminate(),this.videoTransformWorker=null),this.audioTransformWorker&&(this.audioTransformWorker.postMessage({type:"stop"}),this.audioTransformWorker.terminate(),this.audioTransformWorker=null),this.log("Encoder transform detached")}cleanupWriters(){if(this.videoWriter){try{this.videoWriter.releaseLock()}catch{}this.videoWriter=null}if(this.audioWriter){try{this.audioWriter.releaseLock()}catch{}this.audioWriter=null}this.videoWriteQueue=[],this.audioWriteQueue=[],this.isProcessingVideoQueue=!1,this.isProcessingAudioQueue=!1}cleanup(){this.cleanupWriters(),this.detachEncoderTransform(),this.videoTrackGenerator&&(this.videoTrackGenerator.stop(),this.videoTrackGenerator=null),this.audioTrackGenerator&&(this.audioTrackGenerator.stop(),this.audioTrackGenerator=null),this.peerConnection&&(this.peerConnection.close(),this.peerConnection=null),this.resourceUrl=null}async disconnect(){if(this.log("Disconnecting WHIP"),this.resourceUrl){try{this.log("Sending DELETE to WHIP resource:",this.resourceUrl),await fetch(this.resourceUrl,{method:"DELETE"})}catch(e){this.log("Failed to delete WHIP resource (non-fatal)",e)}this.resourceUrl=null}this.cleanup(),this.setState("disconnected")}destroy(){this.cleanup(),this.removeAllListeners()}}class We extends Ae{constructor(e={}){super(),this.audioContext=null,this.destination=null,this.masterGain=null,this.compressor=null,this.limiter=null,this.analyzer=null,this.sources=new Map,this.outputStream=null,this.levelMonitoringActive=!1,this.peakLevel=0,this.peakDecayRate=.95,this.config={sampleRate:e.sampleRate??48e3,channelCount:e.channelCount??2}}async initialize(){if(!this.audioContext)try{this.audioContext=new AudioContext({sampleRate:this.config.sampleRate}),this.destination=this.audioContext.createMediaStreamDestination(),this.destination.channelCount=this.config.channelCount,this.masterGain=this.audioContext.createGain(),this.compressor=this.audioContext.createDynamicsCompressor(),this.compressor.threshold.value=-24,this.compressor.knee.value=30,this.compressor.ratio.value=4,this.compressor.attack.value=.003,this.compressor.release.value=.25,this.limiter=this.audioContext.createDynamicsCompressor(),this.limiter.threshold.value=-1,this.limiter.knee.value=0,this.limiter.ratio.value=20,this.limiter.attack.value=.001,this.limiter.release.value=.1,this.analyzer=this.audioContext.createAnalyser(),this.analyzer.fftSize=256,this.analyzer.smoothingTimeConstant=.3,this.masterGain.connect(this.compressor),this.compressor.connect(this.analyzer),this.analyzer.connect(this.limiter),this.limiter.connect(this.destination),this.outputStream=this.destination.stream,console.log("[AudioMixer] Initialized with compressor/limiter chain",{sampleRate:this.audioContext.sampleRate,channelCount:this.config.channelCount})}catch(e){const t=e instanceof Error?e:new Error(String(e));throw console.error("[AudioMixer] Failed to initialize:",t),this.emit("error",{message:t.message,error:t}),t}}addSource(e,t,r={}){if(!this.audioContext||!this.masterGain)throw new Error("AudioMixer not initialized. Call initialize() first.");if("audio"!==t.kind)throw new Error("Track must be an audio track");this.sources.has(e)&&this.removeSource(e);try{const i=new MediaStream([t]),s=this.audioContext.createMediaStreamSource(i),o=this.audioContext.createGain();o.gain.value=r.muted?0:r.volume??1;const n=this.audioContext.createStereoPanner();n.pan.value=r.pan??0,s.connect(o),o.connect(n),n.connect(this.masterGain);const a={id:e,sourceNode:s,gainNode:o,panNode:n,track:t,options:{volume:r.volume??1,muted:r.muted??!1,pan:r.pan??0}};this.sources.set(e,a),console.log("[AudioMixer] Added source:",e),this.emit("sourceAdded",{sourceId:e})}catch(e){const t=e instanceof Error?e:new Error(String(e));throw console.error("[AudioMixer] Failed to add source:",t),this.emit("error",{message:`Failed to add source: ${t.message}`,error:t}),t}}removeSource(e){const t=this.sources.get(e);if(t)try{t.sourceNode.disconnect(),t.gainNode.disconnect(),t.panNode.disconnect(),this.sources.delete(e),console.log("[AudioMixer] Removed source:",e),this.emit("sourceRemoved",{sourceId:e})}catch(e){console.error("[AudioMixer] Error removing source:",e)}}updateSource(e,t){const r=this.sources.get(e);r?(void 0!==t.volume&&(r.options.volume=t.volume,r.options.muted||r.gainNode.gain.setTargetAtTime(t.volume,this.audioContext?.currentTime??0,.01)),void 0!==t.muted&&(r.options.muted=t.muted,r.gainNode.gain.setTargetAtTime(t.muted?0:r.options.volume,this.audioContext?.currentTime??0,.01)),void 0!==t.pan&&(r.options.pan=t.pan,r.panNode.pan.setTargetAtTime(t.pan,this.audioContext?.currentTime??0,.01))):console.warn("[AudioMixer] Source not found:",e)}setVolume(e,t){this.updateSource(e,{volume:Math.max(0,Math.min(2,t))})}mute(e){this.updateSource(e,{muted:!0})}unmute(e){this.updateSource(e,{muted:!1})}toggleMute(e){const t=this.sources.get(e);if(!t)return!1;const r=!t.options.muted;return this.updateSource(e,{muted:r}),r}setPan(e,t){this.updateSource(e,{pan:Math.max(-1,Math.min(1,t))})}setMasterVolume(e){this.masterGain&&this.masterGain.gain.setTargetAtTime(Math.max(0,Math.min(2,e)),this.audioContext?.currentTime??0,.01)}getMasterVolume(){return this.masterGain?.gain.value??1}getOutputStream(){return this.outputStream}getOutputTrack(){return this.outputStream?.getAudioTracks()[0]??null}getSourceIds(){return Array.from(this.sources.keys())}getSourceOptions(e){const t=this.sources.get(e);return t?{...t.options}:null}hasSource(e){return this.sources.has(e)}getSourceCount(){return this.sources.size}async resume(){this.audioContext&&"suspended"===this.audioContext.state&&await this.audioContext.resume()}async suspend(){this.audioContext&&"running"===this.audioContext.state&&await this.audioContext.suspend()}getState(){return this.audioContext?.state??null}getLevel(){if(!this.analyzer)return 0;const e=new Uint8Array(this.analyzer.frequencyBinCount);this.analyzer.getByteTimeDomainData(e);let t=0;for(let r=0;r<e.length;r++){const i=Math.abs(e[r]-128)/128;i>t&&(t=i)}if(t<1e-4)return 0;const r=(20*Math.log10(t)- -60)/60;return Math.max(0,Math.min(1,r))}getLevels(){const e=this.getLevel();return e>this.peakLevel?this.peakLevel=e:this.peakLevel*=this.peakDecayRate,{level:e,peakLevel:this.peakLevel}}startLevelMonitoring(){if(this.levelMonitoringActive)return;this.levelMonitoringActive=!0;const e=()=>{if(!this.levelMonitoringActive||"running"!==this.audioContext?.state)return;const{level:t,peakLevel:r}=this.getLevels();this.emit("levelUpdate",{level:t,peakLevel:r}),requestAnimationFrame(e)};requestAnimationFrame(e),console.log("[AudioMixer] Level monitoring started")}stopLevelMonitoring(){this.levelMonitoringActive=!1,this.peakLevel=0,console.log("[AudioMixer] Level monitoring stopped")}isMonitoringLevels(){return this.levelMonitoringActive}destroy(){this.stopLevelMonitoring();for(const e of this.sources.keys())this.removeSource(e);this.compressor&&(this.compressor.disconnect(),this.compressor=null),this.limiter&&(this.limiter.disconnect(),this.limiter=null),this.analyzer&&(this.analyzer.disconnect(),this.analyzer=null),this.audioContext&&(this.audioContext.close().catch(()=>{}),this.audioContext=null),this.destination=null,this.masterGain=null,this.outputStream=null,this.removeAllListeners()}}const ze={enabled:!0,maxAttempts:5,baseDelay:1e3,maxDelay:3e4,backoffMultiplier:2};class Ue extends Ae{constructor(e={}){super(),this.reconnectTimeout=null,this.countdownInterval=null,this.reconnectCallback=null,this.config={...ze,...e},this.state={isReconnecting:!1,attemptNumber:0,nextAttemptIn:null,lastError:null}}calculateDelay(e){const t=this.config.baseDelay*Math.pow(this.config.backoffMultiplier,e-1),r=.1*t*(2*Math.random()-1);return Math.min(t+r,this.config.maxDelay)}start(e){this.config.enabled?this.state.isReconnecting?console.log("[ReconnectionManager] Already reconnecting"):(this.reconnectCallback=e,this.state={isReconnecting:!0,attemptNumber:0,nextAttemptIn:null,lastError:null},this.scheduleNextAttempt()):console.log("[ReconnectionManager] Reconnection disabled")}scheduleNextAttempt(){if(!this.state.isReconnecting)return;if(this.state.attemptNumber++,this.state.attemptNumber>this.config.maxAttempts)return void this.handleExhausted();const e=this.calculateDelay(this.state.attemptNumber);this.state.nextAttemptIn=e,console.log(`[ReconnectionManager] Scheduling attempt ${this.state.attemptNumber}/${this.config.maxAttempts} in ${e}ms`),this.emit("attemptStart",{attempt:this.state.attemptNumber,delay:e}),this.startCountdown(e),this.reconnectTimeout=setTimeout(()=>{this.executeAttempt()},e)}startCountdown(e){this.stopCountdown();const t=Date.now();this.countdownInterval=setInterval(()=>{const r=Date.now()-t;this.state.nextAttemptIn=Math.max(0,e-r)},100)}stopCountdown(){this.countdownInterval&&(clearInterval(this.countdownInterval),this.countdownInterval=null)}async executeAttempt(){if(this.stopCountdown(),this.state.nextAttemptIn=null,this.reconnectCallback){console.log(`[ReconnectionManager] Executing attempt ${this.state.attemptNumber}`);try{await this.reconnectCallback(),this.handleSuccess()}catch(e){const t=e instanceof Error?e.message:String(e);this.handleFailure(t)}}else console.error("[ReconnectionManager] No reconnect callback set")}handleSuccess(){console.log("[ReconnectionManager] Reconnection successful"),this.state={isReconnecting:!1,attemptNumber:0,nextAttemptIn:null,lastError:null},this.cleanup(),this.emit("attemptSuccess",void 0)}handleFailure(e){console.log(`[ReconnectionManager] Attempt ${this.state.attemptNumber} failed:`,e),this.state.lastError=e,this.emit("attemptFailed",{attempt:this.state.attemptNumber,error:e}),this.scheduleNextAttempt()}handleExhausted(){console.log("[ReconnectionManager] All reconnection attempts exhausted"),this.emit("exhausted",{totalAttempts:this.config.maxAttempts}),this.stop()}stop(){console.log("[ReconnectionManager] Stopping reconnection"),this.cleanup(),this.state={isReconnecting:!1,attemptNumber:0,nextAttemptIn:null,lastError:this.state.lastError}}cleanup(){this.reconnectTimeout&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null),this.stopCountdown(),this.reconnectCallback=null}reset(){this.stop(),this.state.lastError=null}getState(){return{...this.state}}isReconnecting(){return this.state.isReconnecting}getAttemptNumber(){return this.state.attemptNumber}getMaxAttempts(){return this.config.maxAttempts}updateConfig(e){this.config={...this.config,...e}}destroy(){this.cleanup(),this.removeAllListeners()}}const Le=[{mode:"solo",label:"Solo",icon:"⬜",minSources:1,maxSources:1},{mode:"pip-br",label:"PiP ↘",icon:"◳",minSources:2,maxSources:2},{mode:"pip-bl",label:"PiP ↙",icon:"◲",minSources:2,maxSources:2},{mode:"pip-tr",label:"PiP ↗",icon:"◱",minSources:2,maxSources:2},{mode:"pip-tl",label:"PiP ↖",icon:"◰",minSources:2,maxSources:2},{mode:"split-h",label:"Split ⬌",icon:"▥",minSources:2,maxSources:2},{mode:"split-v",label:"Split ⬍",icon:"▤",minSources:2,maxSources:2},{mode:"focus-l",label:"Focus ◀",icon:"◧",minSources:2,maxSources:2},{mode:"focus-r",label:"Focus ▶",icon:"◨",minSources:2,maxSources:2},{mode:"pip-dual-br",label:"Main+2 PiP",icon:"⊞",minSources:3,maxSources:3},{mode:"pip-dual-bl",label:"Main+2 PiP ↙",icon:"⊟",minSources:3,maxSources:3},{mode:"split-pip-l",label:"Split+PiP",icon:"⊠",minSources:3,maxSources:3},{mode:"split-pip-r",label:"Split+PiP ▶",icon:"⊡",minSources:3,maxSources:3},{mode:"featured",label:"Featured",icon:"⬒",minSources:3,maxSources:99},{mode:"featured-r",label:"Featured ▶",icon:"⬓",minSources:3,maxSources:99},{mode:"grid",label:"Grid",icon:"▦",minSources:2,maxSources:99},{mode:"stack",label:"Stack",icon:"☰",minSources:2,maxSources:99}];let De="letterbox";function Ve(e,t,r,i=De){return{id:`layer-${t}-${e}`,sourceId:e,visible:!0,locked:!1,zIndex:t,transform:{...Te,...r},scalingMode:i}}const Oe=.25,je=.02,Ne=.005;function Fe(e){return[Ve(e[0],0,{x:0,y:0,width:1,height:1})]}function He(e,t,r=.25){if(e.length<2)return Fe(e);const i={tl:{x:je,y:je},tr:{x:1-r-je,y:je},bl:{x:je,y:1-r-je},br:{x:1-r-je,y:1-r-je}}[t];return[Ve(e[0],0,{x:0,y:0,width:1,height:1}),Ve(e[1],1,{x:i.x,y:i.y,width:r,height:r,borderRadius:8})]}function qe(e,t,r=.5){if(e.length<2)return Fe(e);const i=.0025;return"h"===t?[Ve(e[0],0,{x:0,y:0,width:r-i,height:1}),Ve(e[1],0,{x:r+i,y:0,width:1-r-i,height:1})]:[Ve(e[0],0,{x:0,y:0,width:1,height:r-i}),Ve(e[1],0,{x:0,y:r+i,width:1,height:1-r-i})]}function Be(e,t){if(e.length<3)return He(e,"br"===t?"br":"bl");const r=Oe,i=[Ve(e[0],0,{x:0,y:0,width:1,height:1})];return"br"===t?i.push(Ve(e[1],1,{x:.73,y:.47,width:r,height:r,borderRadius:8}),Ve(e[2],2,{x:.73,y:.73,width:r,height:r,borderRadius:8})):i.push(Ve(e[1],1,{x:je,y:.47,width:r,height:r,borderRadius:8}),Ve(e[2],2,{x:je,y:.73,width:r,height:r,borderRadius:8})),i}function Ge(e,t){if(e.length<3)return qe(e,"h");const r=Oe,i=[Ve(e[0],0,{x:0,y:0,width:.4975,height:1}),Ve(e[1],0,{x:.5025,y:0,width:.4975,height:1})];return"l"===t?i.push(Ve(e[2],1,{x:.2275,y:.73,width:r,height:r,borderRadius:8})):i.push(Ve(e[2],1,{x:.73,y:.73,width:r,height:r,borderRadius:8})),i}function Qe(e,t){const r=e.length;if(r<=2)return qe(e,"bottom"===t?"v":"h",.75);const i=Ne,s=.795,o=[];if("bottom"===t){o.push(Ve(e[0],0,{x:0,y:0,width:1,height:s}));const t=r-1,n=(1-i*(t-1))/t;for(let t=1;t<r;t++)o.push(Ve(e[t],0,{x:(t-1)*(n+i),y:.8,width:n,height:.2}))}else{o.push(Ve(e[0],0,{x:0,y:0,width:s,height:1}));const t=r-1,n=(1-i*(t-1))/t;for(let t=1;t<r;t++)o.push(Ve(e[t],0,{x:.8,y:(t-1)*(n+i),width:.2,height:n}))}return o}function Xe(e,t){if(0===t.length)return[];De=e.scalingMode??"letterbox";const r=e.pipScale??Oe,i=e.splitRatio??.5,s=Math.min(Math.max(i,.0025),.9975);switch(e.mode){case"solo":case"fullscreen":default:return Fe(t);case"pip-br":case"pip":return He(t,"br",r);case"pip-bl":return He(t,"bl",r);case"pip-tr":return He(t,"tr",r);case"pip-tl":return He(t,"tl",r);case"split-h":case"side-by-side":return qe(t,"h",s);case"split-v":return qe(t,"v",s);case"focus-l":return qe(t,"h",.7);case"focus-r":return qe(t,"h",.3);case"pip-dual-br":return Be(t,"br");case"pip-dual-bl":return Be(t,"bl");case"split-pip-l":return Ge(t,"l");case"split-pip-r":return Ge(t,"r");case"featured":return Qe(t,"bottom");case"featured-r":return Qe(t,"right");case"grid":return function(e){const t=e.length;if(0===t)return[];if(1===t)return Fe(e);if(2===t)return qe(e,"h");const r=Ne,i=Math.ceil(Math.sqrt(t)),s=Math.ceil(t/i),o=(1-r*(i-1))/i,n=(1-r*(s-1))/s,a=[];for(let c=0;c<t;c++){const l=c%i,d=Math.floor(c/i),h=d===s-1,u=h?(t-1)%i+1:i;let p=0;h&&u<i&&(p=(1-(u*o+(u-1)*r))/2);const g=p+l*(o+r),f=d*(n+r);a.push(Ve(e[c],c,{x:g,y:f,width:o,height:n}))}return a}(t);case"stack":return function(e){const t=e.length;if(0===t)return[];if(1===t)return Fe(e);const r=Ne,i=(1-r*(t-1))/t;return e.map((e,t)=>Ve(e,0,{x:0,y:t*(i+r),width:1,height:i}))}(t)}}const Ke={durationMs:300,easing:"ease-out"};class Je extends Ae{constructor(e){super(),this.scenes=new Map,this.activeSceneId=null,this.defaultLayoutTransition=Ke,this.isAnimating=!1,this.worker=null,this.workerReady=!1,this.pendingMessages=[],this.frameProcessors=new Map,this.frameReaders=new Map,this.outputCanvas=null,this.outputStream=null,this.lastStats={fps:0,frameTimeMs:0},this.config={...Me,...e},this.defaultTransition=this.config.defaultTransition||{type:"fade",durationMs:500,easing:"ease-in-out"},this.currentLayout={mode:"solo",scalingMode:"letterbox"}}async initializeWorker(){let e=null;const r=[new URL("../workers/compositor.worker.js",t&&"SCRIPT"===t.tagName.toUpperCase()&&t.src||new URL("fw-streamcrafter.iife.js",document.baseURI).href).href,"/node_modules/@livepeer-frameworks/streamcrafter-core/dist/workers/compositor.worker.js","/workers/compositor.worker.js","./workers/compositor.worker.js"];console.log("[SceneManager] Trying fallback worker paths:",r);for(const t of r){if(this.worker)break;try{console.log(`[SceneManager] Trying worker path: ${t}`);const e=(()=>{try{return new Worker(t,{type:"module"})}catch{return new Worker(t)}})();if(await new Promise(t=>{const r=setTimeout(()=>{e.terminate(),t(!1)},2e3);e.onerror=()=>{clearTimeout(r),e.terminate(),t(!1)},e.onmessage=()=>{clearTimeout(r),t(!0)},setTimeout(()=>{clearTimeout(r),t(!0)},500)})){e.terminate();try{this.worker=new Worker(t,{type:"module"})}catch{this.worker=new Worker(t)}console.log(`[SceneManager] Worker loaded from: ${t}`);break}console.warn(`[SceneManager] Worker failed to load from: ${t}`)}catch(r){e=r instanceof Error?r:new Error(String(r)),console.warn(`[SceneManager] Failed to load worker from ${t}:`,e.message),this.worker=null}}if(!this.worker)throw new Error(`Failed to initialize compositor worker. Make sure the worker is bundled correctly. Last error: ${e?.message??"unknown"}`)}async initialize(){if(console.log("[SceneManager] initialize() called"),this.worker)throw new Error("SceneManager already initialized");console.log("[SceneManager] Creating output canvas",{width:this.config.width,height:this.config.height}),this.outputCanvas=document.createElement("canvas"),this.outputCanvas.width=this.config.width,this.outputCanvas.height=this.config.height;const e=this.outputCanvas.transferControlToOffscreen();console.log("[SceneManager] Created OffscreenCanvas"),console.log("[SceneManager] Initializing worker..."),await this.initializeWorker(),console.log("[SceneManager] Worker initialized, waiting for ready...");const t=new Promise((e,t)=>{const r=setTimeout(()=>{console.error("[SceneManager] Worker initialization timeout"),t(new Error("Compositor worker initialization timeout"))},1e4);this.worker.onmessage=t=>{console.log("[SceneManager] Worker message:",t.data.type),"ready"===t.data.type&&(clearTimeout(r),e()),this.handleWorkerMessage(t.data)},this.worker.onerror=e=>{console.error("[SceneManager] Worker error:",e.message),clearTimeout(r),t(new Error(e.message))}});console.log("[SceneManager] Sending init message to worker"),this.worker.postMessage({type:"init",config:this.config,canvas:e},[e]),await t,console.log("[SceneManager] Worker is ready"),console.log("[SceneManager] Creating default scene");const r=this.createScene("Default");console.log("[SceneManager] Setting active scene:",r.id),this.setActiveScene(r.id),console.log("[SceneManager] Initialize complete")}handleWorkerMessage(e){switch(e.type){case"ready":this.workerReady=!0;for(const e of this.pendingMessages)this.worker?.postMessage(e);this.pendingMessages=[];break;case"stats":this.lastStats=e.stats,this.emit("statsUpdate",{stats:e.stats});break;case"transitionComplete":this.emit("transitionCompleted",{sceneId:e.sceneId});break;case"layoutAnimationComplete":{this.isAnimating=!1;const e=this.getActiveScene();if(e){const t=e.layers.filter(e=>e.visible).map(e=>e.sourceId);e.layers=Xe(this.currentLayout,t)}this.emit("layoutAnimationCompleted",{layout:this.currentLayout});break}case"rendererChanged":this.config.renderer=e.renderer,this.emit("rendererChanged",{renderer:e.renderer});break;case"error":this.emit("error",{message:e.message})}}sendToWorker(e,t){this.workerReady&&this.worker?this.worker.postMessage(e,t||[]):this.pendingMessages.push(e)}createScene(e,t="#000000"){const r=`scene-${Date.now()}-${Math.random().toString(36).slice(2,9)}`,i={id:r,name:e,layers:[],backgroundColor:t};return this.scenes.set(r,i),this.emit("sceneCreated",{scene:i}),i}deleteScene(e){if(!this.scenes.has(e))throw new Error(`Scene not found: ${e}`);if(this.activeSceneId===e)throw new Error("Cannot delete the active scene");this.scenes.delete(e),this.emit("sceneDeleted",{sceneId:e})}getScene(e){return this.scenes.get(e)}getAllScenes(){return Array.from(this.scenes.values())}getActiveScene(){return this.activeSceneId?this.scenes.get(this.activeSceneId):void 0}setActiveScene(e){const t=this.scenes.get(e);if(!t)throw new Error(`Scene not found: ${e}`);const r=this.activeSceneId;this.activeSceneId=e,this.sendToWorker({type:"updateScene",scene:t}),this.emit("sceneActivated",{scene:t,previousSceneId:r})}async transitionTo(e,t){const r=this.scenes.get(e);if(!r)throw new Error(`Scene not found: ${e}`);const i=t||this.defaultTransition;this.sendToWorker({type:"updateScene",scene:r}),this.sendToWorker({type:"startTransition",transition:i,toSceneId:e});const s=this.activeSceneId;this.emit("transitionStarted",{fromSceneId:s||"",toSceneId:e,transition:i}),this.activeSceneId=e}addLayer(e,t,r){const i=this.scenes.get(e);if(!i)throw new Error(`Scene not found: ${e}`);const s=`layer-${Date.now()}-${Math.random().toString(36).slice(2,9)}`,o=i.layers.reduce((e,t)=>Math.max(e,t.zIndex),-1),n={id:s,sourceId:t,visible:!0,locked:!1,zIndex:o+1,transform:{...Te,...r},scalingMode:this.currentLayout.scalingMode??"letterbox"};return i.layers.push(n),this.updateSceneInWorker(i),this.emit("layerAdded",{sceneId:e,layer:n}),n}removeLayer(e,t){const r=this.scenes.get(e);if(!r)throw new Error(`Scene not found: ${e}`);const i=r.layers.findIndex(e=>e.id===t);if(-1===i)throw new Error(`Layer not found: ${t}`);r.layers.splice(i,1),this.updateSceneInWorker(r),this.emit("layerRemoved",{sceneId:e,layerId:t})}updateLayerTransform(e,t,r){const i=this.scenes.get(e);if(!i)throw new Error(`Scene not found: ${e}`);const s=i.layers.find(e=>e.id===t);if(!s)throw new Error(`Layer not found: ${t}`);s.transform={...s.transform,...r},this.updateSceneInWorker(i),this.emit("layerUpdated",{sceneId:e,layer:s})}setLayerVisibility(e,t,r){const i=this.scenes.get(e);if(!i)throw new Error(`Scene not found: ${e}`);const s=i.layers.find(e=>e.id===t);if(!s)throw new Error(`Layer not found: ${t}`);s.visible=r,this.updateSceneInWorker(i),this.emit("layerUpdated",{sceneId:e,layer:s})}reorderLayers(e,t){const r=this.scenes.get(e);if(!r)throw new Error(`Scene not found: ${e}`);t.forEach((e,t)=>{const i=r.layers.find(t=>t.id===e);i&&(i.zIndex=t)}),this.updateSceneInWorker(r)}cycleSourceOrder(e="forward"){const t=this.getActiveScene();if(!t||t.layers.length<2)return void console.warn("[SceneManager] cycleSourceOrder: Need at least 2 layers");console.log("[SceneManager] cycleSourceOrder BEFORE:",{direction:e,layers:t.layers.map(e=>({id:e.id,sourceId:e.sourceId,zIndex:e.zIndex}))});const r=[...t.layers].sort((e,t)=>e.zIndex-t.zIndex),i=r.map(e=>e.id);if(console.log("[SceneManager] sorted layerIds before rotate:",[...i]),"forward"===e){const e=i.shift();e&&i.push(e)}else{const e=i.pop();e&&i.unshift(e)}console.log("[SceneManager] layerIds after rotate:",[...i]),this.reorderLayers(t.id,i),console.log("[SceneManager] after reorderLayers:",{layers:t.layers.map(e=>({id:e.id,sourceId:e.sourceId,zIndex:e.zIndex}))}),this.currentLayout&&this.applyLayout(this.currentLayout,!0,{durationMs:200,easing:"ease-out"}),console.log("[SceneManager] cycleSourceOrder AFTER applyLayout:",{layers:t.layers.map(e=>({id:e.id,sourceId:e.sourceId,zIndex:e.zIndex}))}),this.emit("layerUpdated",{sceneId:t.id,layer:t.layers[0]})}updateSceneInWorker(e){this.activeSceneId===e.id&&this.sendToWorker({type:"updateScene",scene:e})}applyLayout(e,t=!0,r){const i=this.getActiveScene();if(!i)return void console.warn("[SceneManager] applyLayout: No active scene");this.currentLayout=e;const s=[...i.layers].filter(e=>e.visible).sort((e,t)=>e.zIndex-t.zIndex).map(e=>e.sourceId);console.log("[SceneManager] applyLayout",{mode:e.mode,sourceIds:s,currentLayerCount:i.layers.length,animate:t});const o=Xe(e,s),n={...i,layers:o};if(i.layers=o,t&&o.length>0){const t={...this.defaultLayoutTransition,...r};this.isAnimating=!0,this.emit("layoutAnimationStarted",{layout:e}),this.sendToWorker({type:"animateLayout",targetScene:n,transition:t})}else this.updateSceneInWorker(i);this.sendToWorker({type:"updateLayout",layout:e})}setDefaultLayoutTransition(e){this.defaultLayoutTransition={...this.defaultLayoutTransition,...e}}isLayoutAnimating(){return this.isAnimating}getCurrentLayout(){return this.currentLayout}bindSource(e,t){const r=t.getVideoTracks()[0];if(!r)throw new Error("No video track in stream");const i=globalThis.MediaStreamTrackProcessor;if(!i)return void console.warn("[SceneManager] MediaStreamTrackProcessor not available, compositor will not work");const s=new i({track:r});this.frameProcessors.set(e,s);const o=s.readable.getReader();this.frameReaders.set(e,o);const n=async()=>{try{const{done:t,value:r}=await o.read();if(t||!r)return;const i=r;this.sendToWorker({type:"sourceFrame",sourceId:e,frame:i},[i]),n()}catch(e){"AbortError"!==e?.name&&console.error("[SceneManager] Frame read error:",e)}};n()}unbindSource(e){const t=this.frameReaders.get(e);t&&(t.cancel().catch(()=>{}),this.frameReaders.delete(e));this.frameProcessors.get(e)&&this.frameProcessors.delete(e)}async bindImageSource(e,t){const r=await fetch(t),i=await r.blob(),s=await createImageBitmap(i);this.sendToWorker({type:"sourceImage",sourceId:e,bitmap:s},[s])}applyFilter(e,t){this.sendToWorker({type:"applyFilter",layerId:e,filter:t})}getOutputTrack(){return this.outputCanvas?(this.outputStream||(this.outputStream=this.outputCanvas.captureStream(this.config.frameRate)),this.outputStream.getVideoTracks()[0]||null):null}getOutputStream(){return this.outputCanvas?(this.outputStream||(this.outputStream=this.outputCanvas.captureStream(this.config.frameRate)),this.outputStream):null}getRendererType(){return this.config.renderer}setRenderer(e){this.config.renderer=e,this.sendToWorker({type:"setRenderer",renderer:e})}getStats(){return this.lastStats}getConfig(){return{...this.config}}updateOutputConfig(e){const t=e.width??this.config.width,r=e.height??this.config.height,i=e.frameRate??this.config.frameRate;return(t!==this.config.width||r!==this.config.height||i!==this.config.frameRate)&&(this.config.width=t,this.config.height=r,this.config.frameRate=i,this.sendToWorker({type:"resize",width:t,height:r,frameRate:i}),!0)}isInitialized(){return this.workerReady}destroy(){for(const[e]of this.frameProcessors)this.unbindSource(e);this.frameProcessors.clear(),this.frameReaders.clear(),this.worker&&(this.sendToWorker({type:"destroy"}),this.worker.terminate(),this.worker=null),this.scenes.clear(),this.activeSceneId=null,this.outputStream=null,this.outputCanvas=null,this.workerReady=!1,this.removeAllListeners()}}const Ze={professional:{codec:"avc1.4d0032",width:1920,height:1080,bitrate:8e6,framerate:30},broadcast:{codec:"avc1.4d0028",width:1920,height:1080,bitrate:45e5,framerate:30},conference:{codec:"avc1.4d001f",width:1280,height:720,bitrate:25e5,framerate:30},low:{codec:"avc1.42001e",width:640,height:480,bitrate:1e6,framerate:24}},Ye={codec:"opus",sampleRate:48e3,numberOfChannels:2,bitrate:128e3};class et extends Ae{constructor(e={}){super(),this.worker=null,this.videoProcessor=null,this.audioProcessor=null,this.videoReader=null,this.audioReader=null,this.isRunning=!1,this.isInitialized=!1,this.stats=null,this.config=null,this.pendingRequests=new Map,this.requestCounter=0,this.providedWorker=null,this.options={workerUrl:e.workerUrl??"",debug:e.debug??!1,timeout:e.timeout??1e4},this.providedWorker=e.worker??null}log(e,t){this.options.debug&&console.log(`[EncoderManager] ${e}`,t??"")}generateRequestId(){return`req_${++this.requestCounter}_${Date.now()}`}sendRequest(e){return new Promise((t,r)=>{if(!this.worker)return void r(new Error("Worker not created"));const i=e.requestId;if(!i)return this.worker.postMessage(e),void t(void 0);const s=setTimeout(()=>{this.pendingRequests.delete(i),r(new Error(`Request ${i} timed out`))},this.options.timeout);this.pendingRequests.set(i,{resolve:t,reject:r,timer:s}),this.worker.postMessage(e)})}handleResponse(e,t,r){const i=this.pendingRequests.get(e);i&&(clearTimeout(i.timer),this.pendingRequests.delete(e),t?i.resolve():i.reject(new Error(r??"Unknown error")))}tryCreateWorker(e,t=!0){return new Promise(r=>{try{const i=new Worker(e,t?{type:"module"}:void 0);let s=!1;const o=()=>{i.removeEventListener("message",n),i.removeEventListener("error",a)},n=e=>{s||(s=!0,o(),r(i))},a=t=>{s||(s=!0,o(),this.log("Worker failed to load from: "+e.toString(),t.message),i.terminate(),r(null))};i.addEventListener("message",n),i.addEventListener("error",a),setTimeout(()=>{s||(s=!0,o(),r(i))},2e3)}catch(t){this.log("Failed to create worker: "+e.toString(),t),r(null)}})}async createWorkerAsync(){if(this.providedWorker)return this.log("Using provided worker instance"),this.providedWorker;if(this.options.workerUrl){this.log("Creating worker from URL:",this.options.workerUrl);const e=await this.tryCreateWorker(this.options.workerUrl);if(e)return e}const e=[];try{const r=new URL("../workers/encoder.worker.js",t&&"SCRIPT"===t.tagName.toUpperCase()&&t.src||new URL("fw-streamcrafter.iife.js",document.baseURI).href);e.push({url:r,description:"import.meta.url relative"})}catch{}e.push({url:"/workers/encoder.worker.js",description:"Vite dev server"},{url:"/node_modules/@livepeer-frameworks/streamcrafter-core/dist/workers/encoder.worker.js",description:"node_modules"},{url:"./workers/encoder.worker.js",description:"relative path"});for(const{url:t,description:r}of e){this.log(`Trying worker path: ${t.toString()} (${r})`);const e=await this.tryCreateWorker(t);if(e)return this.log("Worker loaded from:",t.toString()),e}throw new Error("Failed to create encoder worker. Provide a worker URL via options.workerUrl or ensure workers are served correctly.")}createWorker(){if(this.providedWorker)return this.providedWorker;if(this.options.workerUrl)return new Worker(this.options.workerUrl,{type:"module"});try{const e=new URL("../workers/encoder.worker.js",t&&"SCRIPT"===t.tagName.toUpperCase()&&t.src||new URL("fw-streamcrafter.iife.js",document.baseURI).href);return new Worker(e,{type:"module"})}catch{}return new Worker("/workers/encoder.worker.js",{type:"module"})}handleWorkerMessage(e){switch(e.type){case"ready":this.log("Worker ready"),e.requestId&&this.handleResponse(e.requestId,!0),this.emit("ready",void 0);break;case"started":this.log("Encoding started"),e.requestId&&this.handleResponse(e.requestId,!0),this.emit("started",void 0);break;case"stopped":this.log("Encoding stopped"),e.requestId&&this.handleResponse(e.requestId,!0),this.emit("stopped",void 0);break;case"flushed":this.log("Encoder flushed"),e.requestId&&this.handleResponse(e.requestId,!0);break;case"stats":this.stats=e.data,this.emit("stats",this.stats);break;case"error":{const t=e.data;console.error("[EncoderManager] Worker error:",t),e.requestId&&this.handleResponse(e.requestId,!1,t.message),this.emit("error",t);break}case"encodedVideoChunk":this.emit("videoChunk",e.data);break;case"encodedAudioChunk":this.emit("audioChunk",e.data);break;default:this.log("Unknown message from worker",e)}}async initialize(e,t){if(this.log("Initializing encoder",t),this.config=t,"undefined"==typeof VideoEncoder||"undefined"==typeof AudioEncoder)throw new Error("WebCodecs not supported in this browser");if("undefined"==typeof MediaStreamTrackProcessor)throw new Error("MediaStreamTrackProcessor not supported in this browser");const r=e.getVideoTracks()[0],i=e.getAudioTracks()[0];r&&(this.videoProcessor=new MediaStreamTrackProcessor({track:r})),i&&(this.audioProcessor=new MediaStreamTrackProcessor({track:i})),this.worker=await this.createWorkerAsync(),this.worker.onmessage=e=>{this.handleWorkerMessage(e.data)},this.worker.onerror=e=>{console.error("[EncoderManager] Worker error:",e),this.emit("error",{message:e.message,fatal:!0})};const s=this.generateRequestId();this.log("Sending initialize to worker",{requestId:s}),await this.sendRequest({type:"initialize",requestId:s,data:{config:t}}),this.isInitialized=!0,this.log("Worker initialized and ready")}async start(){if(!this.isInitialized)throw new Error("EncoderManager not initialized");if(this.isRunning)return;this.log("Starting encoder");const e=this.generateRequestId();await this.sendRequest({type:"start",requestId:e}),this.isRunning=!0,this.videoProcessor&&this.startVideoProcessing(),this.audioProcessor&&this.startAudioProcessing(),this.log("Encoder started, frame processing active")}async startVideoProcessing(){if(this.videoProcessor&&this.worker)try{this.videoReader=this.videoProcessor.readable.getReader();const e=this.videoReader;for(;this.isRunning;){const{value:t,done:r}=await e.read();if(r||!t)break;try{this.worker.postMessage({type:"videoFrame",data:t},[t])}catch(e){console.error("[EncoderManager] Error sending video frame:",e);try{t.close()}catch{}}}}catch(e){this.isRunning&&console.error("[EncoderManager] Video processing error:",e)}}async startAudioProcessing(){if(this.audioProcessor&&this.worker)try{this.audioReader=this.audioProcessor.readable.getReader();const e=this.audioReader;for(;this.isRunning;){const{value:t,done:r}=await e.read();if(r||!t)break;try{this.worker.postMessage({type:"audioData",data:t},[t])}catch(e){console.error("[EncoderManager] Error sending audio data:",e);try{t.close()}catch{}}}}catch(e){this.isRunning&&console.error("[EncoderManager] Audio processing error:",e)}}async stop(){if(this.isRunning){if(this.isRunning=!1,this.log("Stopping encoder"),this.videoReader){try{await this.videoReader.cancel(),this.videoReader.releaseLock()}catch{}this.videoReader=null}if(this.audioReader){try{await this.audioReader.cancel(),this.audioReader.releaseLock()}catch{}this.audioReader=null}if(this.worker&&this.isInitialized){const e=this.generateRequestId();try{await this.sendRequest({type:"stop",requestId:e})}catch(e){this.log("Stop request failed (may be expected)",e)}}this.emit("stopped",void 0)}}async updateConfig(e){if(!this.worker||!this.isInitialized)throw new Error("EncoderManager not initialized");const t=this.generateRequestId();await this.sendRequest({type:"updateConfig",requestId:t,data:e}),this.config&&(e.video&&(this.config.video={...this.config.video,...e.video}),e.audio&&(this.config.audio={...this.config.audio,...e.audio}))}async updateInputStream(e){if(!this.isInitialized||!this.worker)throw new Error("EncoderManager not initialized");const t=this.isRunning;if(this.log("Updating input stream (hot-swap)",{wasRunning:t}),this.videoReader){try{await this.videoReader.cancel(),this.videoReader.releaseLock()}catch{}this.videoReader=null}if(this.audioReader){try{await this.audioReader.cancel(),this.audioReader.releaseLock()}catch{}this.audioReader=null}const r=e.getVideoTracks()[0],i=e.getAudioTracks()[0];this.videoProcessor=r?new MediaStreamTrackProcessor({track:r}):null,this.audioProcessor=i?new MediaStreamTrackProcessor({track:i}):null,t&&(this.videoProcessor&&this.startVideoProcessing(),this.audioProcessor&&this.startAudioProcessing(),this.log("Input stream updated, processing restarted"))}async flush(){if(!this.worker||!this.isInitialized)return;const e=this.generateRequestId();await this.sendRequest({type:"flush",requestId:e})}getStats(){return this.stats}getConfig(){return this.config}getIsInitialized(){return this.isInitialized}getIsRunning(){return this.isRunning}destroy(){this.stop(),this.isInitialized=!1,this.videoProcessor=null,this.audioProcessor=null;for(const[,e]of this.pendingRequests)clearTimeout(e.timer),e.reject(new Error("EncoderManager destroyed"));this.pendingRequests.clear(),this.worker&&(this.worker.terminate(),this.worker=null),this.removeAllListeners()}}function tt(){const e={videoEncoder:"undefined"!=typeof VideoEncoder,audioEncoder:"undefined"!=typeof AudioEncoder,mediaStreamTrackProcessor:"undefined"!=typeof MediaStreamTrackProcessor,mediaStreamTrackGenerator:"undefined"!=typeof MediaStreamTrackGenerator};return{webcodecs:e,webrtc:{peerConnection:"undefined"!=typeof RTCPeerConnection,replaceTrack:"undefined"!=typeof RTCRtpSender&&"replaceTrack"in RTCRtpSender.prototype,insertableStreams:"undefined"!=typeof RTCRtpSender&&"createEncodedStreams"in RTCRtpSender.prototype,scriptTransform:"undefined"!=typeof RTCRtpScriptTransform},mediaDevices:{getUserMedia:"undefined"!=typeof navigator&&void 0!==navigator.mediaDevices&&"function"==typeof navigator.mediaDevices.getUserMedia,getDisplayMedia:"undefined"!=typeof navigator&&void 0!==navigator.mediaDevices&&"function"==typeof navigator.mediaDevices.getDisplayMedia,enumerateDevices:"undefined"!=typeof navigator&&void 0!==navigator.mediaDevices&&"function"==typeof navigator.mediaDevices.enumerateDevices},recommended:e.videoEncoder&&e.audioEncoder&&e.mediaStreamTrackProcessor&&e.mediaStreamTrackGenerator?"webcodecs":"mediastream"}}function rt(){return"undefined"!=typeof RTCRtpScriptTransform}function it(){return"undefined"!=typeof VideoEncoder&&"undefined"!=typeof AudioEncoder&&"undefined"!=typeof MediaStreamTrackProcessor&&"undefined"!=typeof MediaStreamTrackGenerator&&rt()}let st=0;class ot extends Ae{constructor(e){super(),this.whipClient=null,this.whipEndpoints=[],this.currentEndpointIndex=0,this.isStoppingIntentionally=!1,this.state="idle",this.stateContext={},this.sources=new Map,this.outputStream=null,this.statsInterval=null,this.lastStats=null,this.statsInFlight=!1,this.sceneManager=null,this.compositorBaseConfig=null,this.encoderManager=null,this.encoderOverrides={},this.config=e,this.currentProfile=e.profile||"broadcast",this.whipEndpoints=this.buildWhipEndpoints(e),this.deviceManager=new Ie,this.screenCapture=new Pe,this.audioMixer=new We,this.reconnectionManager=new Ue(e.reconnection);const t=tt();this.useWebCodecs=e.useWebCodecs??"webcodecs"===t.recommended,this.setupEventForwarding(),this.log("IngestControllerV2 initialized",{useWebCodecs:this.useWebCodecs,profile:this.currentProfile,audioMixing:e.audioMixing??!1})}buildWhipEndpoints(e){if(e.whipUrls&&e.whipUrls.length>0){return[e.whipUrl,...e.whipUrls].filter((e,t,r)=>r.indexOf(e)===t)}return[e.whipUrl]}getCurrentWhipUrl(){return this.whipEndpoints[this.currentEndpointIndex]??this.config.whipUrl}getNextWhipUrl(){return this.whipEndpoints.length>1&&(this.currentEndpointIndex=(this.currentEndpointIndex+1)%this.whipEndpoints.length),this.getCurrentWhipUrl()}log(e,t){this.config.debug&&console.log(`[IngestControllerV2] ${e}`,t??"")}setupEventForwarding(){this.deviceManager.on("devicesChanged",e=>{this.emit("deviceChange",e)}),this.deviceManager.on("error",e=>{this.emit("error",{error:e.message,recoverable:!0})}),this.screenCapture.on("ended",e=>{if(this.log("Screen capture ended",e),e.stream)for(const[t,r]of this.sources)if("screen"===r.type&&r.stream===e.stream){this.removeSource(t);break}}),this.screenCapture.on("error",e=>{this.emit("error",{error:e.message,recoverable:!0})}),this.reconnectionManager.on("attemptStart",e=>{this.emit("reconnectionAttempt",{attempt:e.attempt,maxAttempts:this.reconnectionManager.getMaxAttempts()})}),this.reconnectionManager.on("attemptSuccess",()=>{this.emit("reconnectionSuccess",void 0),this.setState("streaming")}),this.reconnectionManager.on("attemptFailed",e=>{this.log("Reconnection attempt failed",e)}),this.reconnectionManager.on("exhausted",()=>{this.emit("reconnectionFailed",{error:"All reconnection attempts exhausted"}),this.setState("error",{error:"Connection lost - reconnection failed"})})}setState(e,t){this.state=e,t&&(this.stateContext={...this.stateContext,...t}),this.stateContext.sources=Array.from(this.sources.values()),this.stateContext.activeProfile=this.currentProfile,this.stateContext.reconnection=this.reconnectionManager.getState(),this.emit("stateChange",{state:this.state,context:this.stateContext})}async ensureAudioMixer(){this.config.audioMixing&&null===this.audioMixer.getState()&&await this.audioMixer.initialize()}addMediaSource(e,t,r){const i=function(e){return`${e}-${++st}-${Date.now()}`}(e),s=t.getVideoTracks().length>0,o=Array.from(this.sources.values()).filter(e=>e.stream.getVideoTracks().length>0),n=s&&0===o.length,a={id:i,type:e,stream:t,label:r,active:!0,muted:!1,volume:1,primaryVideo:n};if(this.sources.set(i,a),this.log(`Added source: ${i} (${e})`,{label:r,tracks:t.getTracks().length,primaryVideo:n}),this.config.audioMixing){const e=t.getAudioTracks()[0];e&&this.audioMixer.addSource(i,e,{volume:1})}if(this.sceneManager&&this.sceneManager.isInitialized()){this.log("Binding source to compositor",{sourceId:i}),this.sceneManager.bindSource(i,t);const e=this.sceneManager.getActiveScene();this.log("Adding layer to scene",{sourceId:i,activeSceneId:e?.id,sceneLayers:e?.layers.length}),e&&(this.sceneManager.addLayer(e.id,i),this.log("Layer added",{sourceId:i,layerCount:e.layers.length}))}else this.log("Compositor not ready when adding source",{sourceId:i,hasSceneManager:!!this.sceneManager,isInitialized:this.sceneManager?.isInitialized()??!1});return this.emit("sourceAdded",{source:a}),this.updateOutputStreamFromSources(),a}removeSource(e){const t=this.sources.get(e);if(!t)return;const r=t.primaryVideo;if(t.stream.getTracks().forEach(e=>e.stop()),this.config.audioMixing&&this.audioMixer.removeSource(e),this.sceneManager){this.sceneManager.unbindSource(e);const t=this.sceneManager.getActiveScene();if(t){const r=t.layers.find(t=>t.sourceId===e);r&&this.sceneManager.removeLayer(t.id,r.id)}}if(this.sources.delete(e),this.log(`Removed source: ${e}`),r){const e=Array.from(this.sources.values()).filter(e=>e.stream.getVideoTracks().length>0);e.length>0&&(e[0].primaryVideo=!0,this.sources.set(e[0].id,e[0]),this.log(`Reassigned primary video to: ${e[0].id}`))}this.emit("sourceRemoved",{sourceId:e}),this.updateOutputStreamFromSources()}setPrimaryVideoSource(e){const t=this.sources.get(e);if(t)if(0!==t.stream.getVideoTracks().length){for(const[e,t]of this.sources)t.primaryVideo&&(t.primaryVideo=!1,this.sources.set(e,t));t.primaryVideo=!0,this.sources.set(e,t),this.log(`Set primary video source: ${e}`),this.emit("sourceUpdated",{source:t,changes:{primaryVideo:!0}}),this.updateOutputStreamFromSources()}else this.log(`Cannot set source ${e} as primary - no video track`)}getPrimaryVideoSource(){for(const e of this.sources.values())if(e.primaryVideo)return e;return null}updateOutputStreamFromSources(){const e=Array.from(this.sources.values()).filter(e=>e.active);if(0===e.length)return void(this.outputStream=null);const t=[];if(this.sceneManager&&this.sceneManager.isInitialized()){const e=this.sceneManager.getOutputTrack();e&&t.push(e)}else{const r=e.filter(e=>e.stream.getVideoTracks().length>0),i=r.find(e=>e.primaryVideo)||r[0];if(i){const e=i.stream.getVideoTracks()[0];e&&t.push(e)}}if(this.config.audioMixing&&"running"===this.audioMixer.getState()){const e=this.audioMixer.getOutputTrack();e&&t.push(e)}else for(const r of e){const e=r.stream.getAudioTracks()[0];if(e&&!r.muted){t.push(e);break}}this.outputStream=t.length>0?new MediaStream(t):null,this.whipClient&&"streaming"===this.state&&this.updateWhipTracks(),this.encoderManager&&this.outputStream&&this.encoderManager.updateInputStream(this.outputStream).catch(e=>{this.log("Failed to update encoder input stream",e)}),this.log("Output stream updated",{videoTracks:this.outputStream?.getVideoTracks().length??0,audioTracks:this.outputStream?.getAudioTracks().length??0,usingCompositor:!!this.sceneManager})}async updateWhipTracks(){if(this.whipClient&&this.outputStream)try{const e=this.whipClient.getPeerConnection();if(!e)return;const t=e.getSenders(),r=this.outputStream.getVideoTracks()[0],i=t.find(e=>"video"===e.track?.kind);i&&await i.replaceTrack(r??null);const s=this.outputStream.getAudioTracks()[0],o=t.find(e=>"audio"===e.track?.kind);o&&await o.replaceTrack(s??null)}catch(e){this.log("Error updating WHIP tracks",e)}}async startCamera(e={}){this.log("Starting camera capture",e),this.setState("requesting_permissions");try{await this.ensureAudioMixer();const t=e.profile||this.currentProfile,r={...e,profile:t};if(this.encoderOverrides?.video){const e=this.encoderOverrides.video,i=_e(t);r.customConstraints={video:{...i,...e.width&&{width:{ideal:e.width}},...e.height&&{height:{ideal:e.height}},...e.framerate&&{frameRate:{ideal:e.framerate}}},audio:!0},this.log("Using encoder overrides for capture constraints:",r.customConstraints)}const i=await this.deviceManager.getUserMedia(r),s=await this.getCameraLabel(i),o=this.addMediaSource("camera",i,s);return this.setState("capturing",{hasVideo:i.getVideoTracks().length>0,hasAudio:i.getAudioTracks().length>0}),o}catch(e){throw this.setState("error",{error:e instanceof Error?e.message:String(e)}),e}}async getCameraLabel(e){const t=e.getVideoTracks()[0];return t&&t.label||"Camera"}async startScreenShare(e={}){this.log("Starting screen share",e),this.setState("requesting_permissions");try{await this.ensureAudioMixer();const t={...e};if(this.encoderOverrides?.video){const e=this.encoderOverrides.video;t.video={...e.width&&{width:{ideal:e.width}},...e.height&&{height:{ideal:e.height}},...e.framerate&&{frameRate:{ideal:e.framerate}}},this.log("Using encoder overrides for screen capture constraints:",t.video)}const r=await this.screenCapture.start(t);if(r){const e=r.getVideoTracks()[0],t=e?.label||`Screen ${this.screenCapture.getCaptureCount()}`,i=this.addMediaSource("screen",r,t);return this.setState("capturing",{hasVideo:!0,isScreenShare:!0}),i}return this.sources.size>0?this.setState("capturing"):this.setState("idle"),null}catch(e){throw this.setState("error",{error:e instanceof Error?e.message:String(e)}),e}}addCustomSource(e,t){return this.addMediaSource("custom",e,t)}setSourceVolume(e,t){const r=this.sources.get(e);r&&(r.volume=Math.max(0,Math.min(2,t)),this.sources.set(e,r),this.config.audioMixing&&this.audioMixer.setVolume(e,r.volume),this.emit("sourceUpdated",{source:r,changes:{volume:r.volume}}))}setSourceMuted(e,t){const r=this.sources.get(e);r&&(r.muted=t,this.sources.set(e,r),this.config.audioMixing?t?this.audioMixer.mute(e):this.audioMixer.unmute(e):r.stream.getAudioTracks().forEach(e=>{e.enabled=!t}),this.emit("sourceUpdated",{source:r,changes:{muted:t}}),this.updateOutputStreamFromSources())}setSourceActive(e,t){const r=this.sources.get(e);r&&(r.active=t,this.sources.set(e,r),this.emit("sourceUpdated",{source:r,changes:{active:t}}),this.updateOutputStreamFromSources())}setMasterVolume(e){this.config.audioMixing&&this.audioMixer.setMasterVolume(e)}getMasterVolume(){return this.config.audioMixing?this.audioMixer.getMasterVolume():1}async stopCapture(){this.log("Stopping all capture");for(const e of Array.from(this.sources.keys()))this.removeSource(e);this.deviceManager.stopAllTracks(),this.screenCapture.stop(),this.outputStream=null,"streaming"!==this.state&&this.setState("idle",{hasVideo:!1,hasAudio:!1,isScreenShare:!1})}async setQualityProfile(e){if(e===this.currentProfile)return;const t=this.currentProfile;this.currentProfile=e,this.log(`Changing quality profile: ${t} -> ${e}`);for(const[t,r]of this.sources)if("camera"===r.type){const t=r.stream.getVideoTracks()[0];if(t)try{const r=_e(e);await t.applyConstraints(r)}catch(e){this.log("Failed to apply new constraints",e)}}this.emit("qualityChanged",{profile:e,previousProfile:t}),this.setState(this.state,{activeProfile:e})}setupWhipClientHandlers(){this.whipClient&&(this.whipClient.on("stateChange",e=>{if(this.log("WHIP state changed",e),this.stateContext={...this.stateContext,connectionState:e.state},"connected"===e.state){if(this.setState("streaming"),this.startStatsPolling(),this.reconnectionManager.reset(),this.useWebCodecs&&this.encoderManager&&this.whipClient){this.log("Attempting to attach WebCodecs encoder transform");const e=this.whipClient.canUseEncodedInsertion();if(this.log("canUseEncodedInsertion result:",e),e)try{this.whipClient.attachEncoderTransform(this.encoderManager),this.encoderManager.start(),this.log("WebCodecs encoder transform attached",{videoCodec:this.whipClient.getNegotiatedVideoCodec(),audioCodec:this.whipClient.getNegotiatedAudioCodec()}),this.emit("webCodecsActive",{active:!0})}catch(e){this.log("Failed to attach encoder transform, continuing with browser encoding",e),this.encoderManager&&(this.encoderManager.destroy(),this.encoderManager=null)}else this.log("Codec alignment check failed, using browser encoding",{videoCodec:this.whipClient.getNegotiatedVideoCodec(),audioCodec:this.whipClient.getNegotiatedAudioCodec()}),this.encoderManager&&(this.encoderManager.destroy(),this.encoderManager=null)}}else if("failed"===e.state||"disconnected"===e.state){if(this.isStoppingIntentionally)return;"streaming"===this.state&&!1!==this.config.reconnection?.enabled?this.handleConnectionLost():(this.setState("error",{error:"failed"===e.state?"Connection failed":"Connection lost"}),this.stopStatsPolling())}}),this.whipClient.on("error",e=>{this.isStoppingIntentionally||this.emit("error",{error:e.message,recoverable:!1})}))}async startStreaming(){if(!this.outputStream)throw new Error("No media source available. Add a camera or screen share first.");this.log("Starting streaming"),this.currentEndpointIndex=0,this.setState("connecting");try{if(this.whipClient=new Re({whipUrl:this.getCurrentWhipUrl(),iceServers:this.config.iceServers,debug:this.config.debug}),this.setupWhipClientHandlers(),this.config.audioMixing&&await this.audioMixer.resume(),this.useWebCodecs&&rt()){this.log("Initializing WebCodecs encoder (Path C: RTCRtpScriptTransform)");try{this.encoderManager=new et({debug:this.config.debug}),this.encoderManager.on("error",e=>{this.emit("error",{error:e.message,recoverable:!e.fatal}),e.fatal&&"streaming"===this.state&&(this.log("Fatal encoder error, reconnecting without WebCodecs"),this.handleEncoderFailure())}),this.encoderManager.on("stats",e=>{this.log("Encoder stats",e)});const e=function(e="broadcast",t){const r=Ze[e],i=Ye,s=t?.video?.width??r.width,o=t?.video?.height??r.height,n=t?.video?.framerate??r.framerate,a=function(e,t,r){const i=e*t;return i>=8294400?r>30?"avc1.640034":"avc1.640033":i>=3686400?r>30?"avc1.640033":"avc1.640032":i>=2073600?r>30?"avc1.640032":"avc1.64002a":i>=921600?r>30?"avc1.64002a":"avc1.640028":"avc1.64001f"}(s,o,n);return{video:{...r,codec:a,width:s,height:o,framerate:n,...void 0!==t?.video?.bitrate&&{bitrate:t.video.bitrate}},audio:{...i,...void 0!==t?.audio?.bitrate&&{bitrate:t.audio.bitrate},...void 0!==t?.audio?.sampleRate&&{sampleRate:t.audio.sampleRate},...void 0!==t?.audio?.numberOfChannels&&{numberOfChannels:t.audio.numberOfChannels}}}}("auto"===this.currentProfile?"broadcast":this.currentProfile,this.encoderOverrides);this.log("Encoder config with overrides:",e),await this.encoderManager.initialize(this.outputStream,e),this.log("WebCodecs encoder initialized")}catch(e){this.log("WebCodecs encoder initialization failed, falling back to browser encoding",e),this.encoderManager&&(this.encoderManager.destroy(),this.encoderManager=null)}}else this.useWebCodecs&&this.log("WebCodecs requested but RTCRtpScriptTransform not supported, using browser encoding");await this.whipClient.connect(this.outputStream)}catch(e){throw this.encoderManager&&(this.encoderManager.destroy(),this.encoderManager=null),this.setState("error",{error:e instanceof Error?e.message:String(e)}),e}}async handleEncoderFailure(){if(this.log("Handling encoder failure - reconnecting without WebCodecs"),this.setState("reconnecting"),this.stopStatsPolling(),this.encoderManager&&(this.encoderManager.destroy(),this.encoderManager=null),this.useWebCodecs=!1,this.whipClient)try{await this.whipClient.disconnect()}finally{this.whipClient.destroy(),this.whipClient=null}if(this.outputStream)try{this.whipClient=new Re({whipUrl:this.getNextWhipUrl(),iceServers:this.config.iceServers,debug:this.config.debug}),this.setupWhipClientHandlers(),await this.whipClient.connect(this.outputStream)}catch(e){this.setState("error",{error:`Reconnection failed: ${e instanceof Error?e.message:String(e)}`})}else this.setState("error",{error:"No output stream available for reconnection"})}handleConnectionLost(){this.log("Connection lost, starting reconnection"),this.setState("reconnecting"),this.stopStatsPolling(),this.reconnectionManager.start(async()=>{if(this.whipClient)try{await this.whipClient.disconnect()}finally{this.whipClient.destroy(),this.whipClient=null}if(!this.outputStream)throw new Error("No output stream available");this.whipClient=new Re({whipUrl:this.getNextWhipUrl(),iceServers:this.config.iceServers,debug:this.config.debug}),this.setupWhipClientHandlers(),await new Promise((e,t)=>{const r=setTimeout(()=>{t(new Error("Connection timeout"))},3e4),i=s=>{"connected"===s.state?(clearTimeout(r),this.whipClient?.off("stateChange",i),e()):"failed"===s.state&&(clearTimeout(r),this.whipClient?.off("stateChange",i),t(new Error("Connection failed")))};this.whipClient.on("stateChange",i),this.whipClient.connect(this.outputStream).catch(t)})})}async stopStreaming(){this.log("Stopping streaming"),this.isStoppingIntentionally=!0;try{this.stopStatsPolling(),this.reconnectionManager.stop(),this.encoderManager&&(await this.encoderManager.stop(),this.encoderManager.destroy(),this.encoderManager=null),this.whipClient&&(await this.whipClient.disconnect(),this.whipClient.destroy(),this.whipClient=null),this.sources.size>0?this.setState("capturing"):this.setState("idle"),this.stateContext={...this.stateContext,connectionState:"disconnected"}}finally{this.isStoppingIntentionally=!1}}async switchVideoDevice(e){const t=await this.deviceManager.replaceVideoTrack(e,this.currentProfile);if(t&&this.whipClient){const e=this.whipClient.getPeerConnection();if(e){const r=e.getSenders().find(e=>"video"===e.track?.kind);r&&await r.replaceTrack(t)}}}async switchAudioDevice(e){const t=await this.deviceManager.replaceAudioTrack(e,this.currentProfile);if(t&&this.whipClient){const e=this.whipClient.getPeerConnection();if(e){const r=e.getSenders().find(e=>"audio"===e.track?.kind);r&&await r.replaceTrack(t)}}}startStatsPolling(){this.statsInterval||(this.statsInterval=setInterval(async()=>{if(!this.statsInFlight){this.statsInFlight=!0;try{const e=await this.getStats();e&&(this.lastStats=e,this.emit("statsUpdate",e))}finally{this.statsInFlight=!1}}},1e3))}stopStatsPolling(){this.statsInterval&&(clearInterval(this.statsInterval),this.statsInterval=null)}async getStats(){if(!this.whipClient)return null;const e=await this.whipClient.getStats();if(!e)return null;const t={video:{bytesSent:0,packetsSent:0,packetsLost:0,framesEncoded:0,framesPerSecond:0,bitrate:0},audio:{bytesSent:0,packetsSent:0,packetsLost:0,bitrate:0},connection:{rtt:0,state:this.whipClient.getPeerConnection()?.connectionState??"new",iceState:this.whipClient.getPeerConnection()?.iceConnectionState??"new"},timestamp:Date.now()},r=this.lastStats;return e.forEach(e=>{if("outbound-rtp"===e.type){const i=e;if("video"===i.kind){if(t.video.bytesSent=i.bytesSent??0,t.video.packetsSent=i.packetsSent??0,t.video.framesEncoded=i.framesEncoded??0,t.video.framesPerSecond=i.framesPerSecond??0,r){const e=(t.timestamp-r.timestamp)/1e3,i=t.video.bytesSent-r.video.bytesSent;t.video.bitrate=Math.round(8*i/e)}}else if("audio"===i.kind&&(t.audio.bytesSent=i.bytesSent??0,t.audio.packetsSent=i.packetsSent??0,r)){const e=(t.timestamp-r.timestamp)/1e3,i=t.audio.bytesSent-r.audio.bytesSent;t.audio.bitrate=Math.round(8*i/e)}}else"candidate-pair"===e.type&&"succeeded"===e.state&&(t.connection.rtt=e.currentRoundTripTime??0)}),t}async enableCompositor(e){if(this.log("enableCompositor called",{alreadyEnabled:!!this.sceneManager}),this.sceneManager)return void this.log("Compositor already enabled");const t={...Me,...this.config.compositor,...e};this.log("Creating SceneManager with config",t),this.sceneManager=new Je(t),this.compositorBaseConfig=t;try{this.log("Initializing SceneManager..."),await this.sceneManager.initialize(),this.log("SceneManager initialized successfully")}catch(e){this.sceneManager=null;const t=e instanceof Error?e.message:String(e);throw this.log("Compositor initialization failed:",t),new Error(`Compositor initialization failed: ${t}`)}if(!this.sceneManager)throw this.log("ERROR: SceneManager was unexpectedly null after initialization"),new Error("SceneManager was unexpectedly null after initialization");this.log("SceneManager is valid, getting active scene...");const r=this.sceneManager.getActiveScene();this.log("Compositor active scene:",r?.id??"none");for(const[e,t]of this.sources){if(!this.sceneManager)break;this.sceneManager.bindSource(e,t.stream),r&&this.sceneManager.addLayer(r.id,e)}this.sceneManager&&this.sceneManager.on("error",e=>{this.emit("error",{error:e.message,recoverable:!0})}),this.log("Compositor enabled",t),this.updateOutputStreamFromSources()}disableCompositor(){this.sceneManager&&(this.sceneManager.destroy(),this.sceneManager=null,this.log("Compositor disabled"),this.updateOutputStreamFromSources())}getSceneManager(){return this.sceneManager}isCompositorEnabled(){return null!==this.sceneManager&&this.sceneManager.isInitialized()}getState(){return this.state}getStateContext(){return{...this.stateContext}}getMediaStream(){return this.outputStream}getSources(){return Array.from(this.sources.values())}getSource(e){return this.sources.get(e)}getQualityProfile(){return this.currentProfile}getDeviceManager(){return this.deviceManager}getScreenCapture(){return this.screenCapture}getAudioMixer(){return this.audioMixer}getReconnectionManager(){return this.reconnectionManager}async getDevices(){return this.deviceManager.enumerateDevices()}isStreaming(){return"streaming"===this.state}isCapturing(){return"capturing"===this.state||"streaming"===this.state}isReconnecting(){return"reconnecting"===this.state}setUseWebCodecs(e){"streaming"!==this.state?(this.useWebCodecs=e,this.log("useWebCodecs set to",e)):this.log("Cannot change useWebCodecs while streaming")}setEncoderOverrides(e){if("streaming"!==this.state){if(this.encoderOverrides=e,this.log("Encoder overrides set:",e),this.sceneManager){const t=this.compositorBaseConfig??this.sceneManager.getConfig(),r=e.video?.width??t.width,i=e.video?.height??t.height,s=e.video?.framerate??t.frameRate;this.sceneManager.updateOutputConfig({width:r,height:i,frameRate:s})&&this.updateOutputStreamFromSources()}}else this.log("Cannot change encoder overrides while streaming")}getEncoderOverrides(){return this.encoderOverrides}getUseWebCodecs(){return this.useWebCodecs}getEncoderManager(){return this.encoderManager}isWebCodecsActive(){return null!==this.encoderManager&&!0===this.whipClient?.hasEncoderTransform()}destroy(){this.log("Destroying IngestControllerV2"),this.stopStatsPolling(),this.reconnectionManager.destroy(),this.encoderManager&&(this.encoderManager.destroy(),this.encoderManager=null),this.whipClient&&(this.whipClient.destroy(),this.whipClient=null),this.sceneManager&&(this.sceneManager.destroy(),this.sceneManager=null);for(const e of Array.from(this.sources.keys()))this.removeSource(e);this.deviceManager.destroy(),this.screenCapture.destroy(),this.audioMixer.destroy(),this.removeAllListeners(),this.setState("destroyed")}}class nt{constructor(e,t="broadcast"){this.controller=null,this.unsubs=[],this.encoderStatsCleanup=null,this.host=e,e.addController(this);const r=tt();this.s={state:"idle",stateContext:{},isStreaming:!1,isCapturing:!1,isReconnecting:!1,error:null,mediaStream:null,sources:[],qualityProfile:t,reconnectionState:null,stats:null,useWebCodecs:"webcodecs"===r.recommended,isWebCodecsActive:!1,isWebCodecsAvailable:it(),encoderStats:null}}initialize(e){this.teardown();const t=new ot({...e,useWebCodecs:this.s.useWebCodecs});this.controller=t,this.subscribeToEvents(t)}hostConnected(){}hostDisconnected(){this.teardown()}teardown(){this.unsubs.forEach(e=>e()),this.unsubs=[],this.encoderStatsCleanup&&(this.encoderStatsCleanup(),this.encoderStatsCleanup=null),this.controller?.destroy(),this.controller=null}update(e){Object.assign(this.s,e),this.host.requestUpdate()}subscribeToEvents(e){const t=this.unsubs;t.push(e.on("stateChange",t=>{const r=t.state,i=t.context??{};this.update({state:r,stateContext:i,isStreaming:"streaming"===r,isCapturing:"capturing"===r||"streaming"===r,isReconnecting:"reconnecting"===r,mediaStream:e.getMediaStream(),sources:e.getSources(),reconnectionState:i.reconnection??this.s.reconnectionState}),this.dispatchEvent("fw-sc-state-change",{state:r,context:i})})),t.push(e.on("statsUpdate",e=>{this.update({stats:e})})),t.push(e.on("error",e=>{this.update({error:e.error}),this.dispatchEvent("fw-sc-error",{error:e.error})})),t.push(e.on("sourceAdded",()=>{this.update({sources:e.getSources(),mediaStream:e.getMediaStream()})})),t.push(e.on("sourceRemoved",()=>{this.update({sources:e.getSources(),mediaStream:e.getMediaStream()})})),t.push(e.on("sourceUpdated",()=>{this.update({sources:e.getSources(),mediaStream:e.getMediaStream()})})),t.push(e.on("qualityChanged",e=>{this.update({qualityProfile:e.profile})})),t.push(e.on("reconnectionAttempt",()=>{this.update({reconnectionState:e.getReconnectionManager().getState()})})),t.push(e.on("webCodecsActive",e=>{this.update({isWebCodecsActive:e.active}),e.active&&this.setupEncoderStatsListener()})),t.push(e.on("stateChange",t=>{"streaming"===t.state?setTimeout(()=>{this.update({isWebCodecsActive:e.isWebCodecsActive()}),e.isWebCodecsActive()&&!this.encoderStatsCleanup&&this.setupEncoderStatsListener()},200):"idle"!==t.state&&"capturing"!==t.state||(this.update({isWebCodecsActive:!1,encoderStats:null}),this.encoderStatsCleanup&&(this.encoderStatsCleanup(),this.encoderStatsCleanup=null))}))}setupEncoderStatsListener(){if(!this.controller)return;const e=this.controller.getEncoderManager();e&&(this.encoderStatsCleanup=e.on("stats",e=>{this.update({encoderStats:e})}))}dispatchEvent(e,t){this.host.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}async startCamera(e){if(!this.controller)throw new Error("Controller not initialized");return this.update({error:null}),this.controller.startCamera(e)}async startScreenShare(e){if(!this.controller)throw new Error("Controller not initialized");return this.update({error:null}),this.controller.startScreenShare(e)}addCustomSource(e,t){if(!this.controller)throw new Error("Controller not initialized");return this.controller.addCustomSource(e,t)}removeSource(e){this.controller?.removeSource(e)}async stopCapture(){await(this.controller?.stopCapture())}setSourceVolume(e,t){this.controller?.setSourceVolume(e,t)}setSourceMuted(e,t){this.controller?.setSourceMuted(e,t)}setSourceActive(e,t){this.controller?.setSourceActive(e,t)}setPrimaryVideoSource(e){this.controller?.setPrimaryVideoSource(e)}setMasterVolume(e){this.controller?.setMasterVolume(e)}getMasterVolume(){return this.controller?.getMasterVolume()??1}async setQualityProfile(e){await(this.controller?.setQualityProfile(e))}async startStreaming(){if(!this.controller)throw new Error("Controller not initialized");this.update({error:null}),await this.controller.startStreaming()}async stopStreaming(){await(this.controller?.stopStreaming())}async getDevices(){return this.controller?.getDevices()??[]}async switchVideoDevice(e){await(this.controller?.switchVideoDevice(e))}async switchAudioDevice(e){await(this.controller?.switchAudioDevice(e))}async getStats(){return this.controller?.getStats()??null}setUseWebCodecs(e){this.update({useWebCodecs:e}),this.controller?.setUseWebCodecs(e)}setEncoderOverrides(e){this.controller?.setEncoderOverrides(e)}getController(){return this.controller}}const at=[{id:"professional",label:"Professional",description:"1080p @ 8 Mbps"},{id:"broadcast",label:"Broadcast",description:"1080p @ 4.5 Mbps"},{id:"conference",label:"Conference",description:"720p @ 2.5 Mbps"}];e.FwStreamCrafter=class extends le{constructor(){super(),this.whipUrl="",this.gatewayUrl="",this.streamKey="",this.initialProfile="broadcast",this.autoStartCamera=!1,this.devMode=!1,this.debug=!1,this.enableCompositor=!1,this._showSettings=!1,this._showSources=!0,this._isAdvancedPanelOpen=!1,this.pc=new nt(this,this.initialProfile)}connectedCallback(){super.connectedCallback(),this._initController()}willUpdate(e){(e.has("whipUrl")||e.has("initialProfile")||e.has("debug"))&&this._initController()}updated(e){e.has("_showSources")||e.has("_showSettings"),this._syncVideoPreview()}_initController(){this.whipUrl&&(this.pc.initialize({whipUrl:this.whipUrl,profile:this.initialProfile,debug:this.debug,reconnection:{enabled:!0,maxAttempts:5},audioMixing:!0}),this.autoStartCamera&&"idle"===this.pc.s.state&&this.pc.startCamera().catch(console.error))}_syncVideoPreview(){const e=this._videoEl,t=this.pc.s.mediaStream;e&&t&&e.srcObject!==t?(e.srcObject=t,e.play().catch(()=>{})):e&&!t&&(e.srcObject=null)}async startCamera(e){return this.pc.startCamera(e)}async startScreenShare(e){return this.pc.startScreenShare(e)}async startStreaming(){return this.pc.startStreaming()}async stopStreaming(){return this.pc.stopStreaming()}async stopCapture(){return this.pc.stopCapture()}removeSource(e){this.pc.removeSource(e)}setSourceVolume(e,t){this.pc.setSourceVolume(e,t)}setSourceMuted(e,t){this.pc.setSourceMuted(e,t)}setPrimaryVideoSource(e){this.pc.setPrimaryVideoSource(e)}setMasterVolume(e){this.pc.setMasterVolume(e)}async setQualityProfile(e){return this.pc.setQualityProfile(e)}destroy(){this.pc.getController()?.destroy()}render(){const e=this.pc.s,t=function(e,t){if(t?.isReconnecting)return`Reconnecting (${t.attemptNumber}/5)...`;switch(e){case"idle":return"Idle";case"requesting_permissions":return"Permissions...";case"capturing":return"Ready";case"connecting":return"Connecting...";case"streaming":return"Live";case"reconnecting":return"Reconnecting...";case"error":return"Error";case"destroyed":return"Destroyed";default:return e}}(e.state,e.reconnectionState),r=(i=e.state,s=e.isReconnecting,"streaming"===i?"fw-sc-badge fw-sc-badge--live":s?"fw-sc-badge fw-sc-badge--connecting":"error"===i?"fw-sc-badge fw-sc-badge--error":"capturing"===i?"fw-sc-badge fw-sc-badge--ready":"fw-sc-badge fw-sc-badge--idle");var i,s;const o="destroyed"!==e.state&&"error"!==e.state,n=e.isCapturing&&!e.isStreaming&&!!this.whipUrl,a=e.sources.some(e=>"camera"===e.type);return q`
|
|
2289
|
+
<div
|
|
2290
|
+
class=${ye({root:!0,"fw-sc-root":!0,"fw-sc-root--devmode":this.devMode})}
|
|
2291
|
+
>
|
|
2292
|
+
<div class="main fw-sc-main">
|
|
2293
|
+
<!-- Header -->
|
|
2294
|
+
<div class="fw-sc-header">
|
|
2295
|
+
<span class="fw-sc-header-title">StreamCrafter</span>
|
|
2296
|
+
<div class="fw-sc-header-status">
|
|
2297
|
+
<span class=${r}>${t}</span>
|
|
2298
|
+
</div>
|
|
2299
|
+
</div>
|
|
2300
|
+
|
|
2301
|
+
<!-- Content -->
|
|
2302
|
+
<div class="fw-sc-content">
|
|
2303
|
+
<div class="fw-sc-preview-wrapper">
|
|
2304
|
+
<div class="fw-sc-preview">
|
|
2305
|
+
<video playsinline muted autoplay aria-label="Stream preview"></video>
|
|
2306
|
+
|
|
2307
|
+
${e.mediaStream?G:q`
|
|
2308
|
+
<div class="fw-sc-preview-placeholder">
|
|
2309
|
+
${Se(48)}
|
|
2310
|
+
<span>Add a camera or screen to preview</span>
|
|
2311
|
+
</div>
|
|
2312
|
+
`}
|
|
2313
|
+
${"connecting"===e.state||"reconnecting"===e.state?q`
|
|
2314
|
+
<div class="fw-sc-status-overlay">
|
|
2315
|
+
<div class="fw-sc-status-spinner"></div>
|
|
2316
|
+
<span class="fw-sc-status-text">${t}</span>
|
|
2317
|
+
</div>
|
|
2318
|
+
`:G}
|
|
2319
|
+
${e.isStreaming?q`<div class="fw-sc-live-badge">Live</div>`:G}
|
|
2320
|
+
${this.enableCompositor?q` <fw-sc-compositor .ic=${this.pc}></fw-sc-compositor> `:G}
|
|
2321
|
+
</div>
|
|
2322
|
+
</div>
|
|
2323
|
+
|
|
2324
|
+
<!-- Sources Mixer -->
|
|
2325
|
+
${e.sources.length>0?q`
|
|
2326
|
+
<div
|
|
2327
|
+
class=${ye({"fw-sc-section":!0,"fw-sc-mixer":!0,"fw-sc-section--collapsed":!this._showSources})}
|
|
2328
|
+
>
|
|
2329
|
+
<div
|
|
2330
|
+
class="fw-sc-section-header"
|
|
2331
|
+
@click=${()=>{this._showSources=!this._showSources}}
|
|
2332
|
+
>
|
|
2333
|
+
<span>Mixer (${e.sources.length})</span>
|
|
2334
|
+
${this._showSources?((e=14)=>q` <svg
|
|
2335
|
+
width="${e}"
|
|
2336
|
+
height="${e}"
|
|
2337
|
+
viewBox="0 0 24 24"
|
|
2338
|
+
fill="none"
|
|
2339
|
+
stroke="currentColor"
|
|
2340
|
+
stroke-width="2"
|
|
2341
|
+
stroke-linecap="round"
|
|
2342
|
+
stroke-linejoin="round"
|
|
2343
|
+
>
|
|
2344
|
+
<polyline points="13 17 18 12 13 7" />
|
|
2345
|
+
<polyline points="6 17 11 12 6 7" />
|
|
2346
|
+
</svg>`)(14):((e=14)=>q` <svg
|
|
2347
|
+
width="${e}"
|
|
2348
|
+
height="${e}"
|
|
2349
|
+
viewBox="0 0 24 24"
|
|
2350
|
+
fill="none"
|
|
2351
|
+
stroke="currentColor"
|
|
2352
|
+
stroke-width="2"
|
|
2353
|
+
stroke-linecap="round"
|
|
2354
|
+
stroke-linejoin="round"
|
|
2355
|
+
>
|
|
2356
|
+
<polyline points="11 17 6 12 11 7" />
|
|
2357
|
+
<polyline points="18 17 13 12 18 7" />
|
|
2358
|
+
</svg>`)(14)}
|
|
2359
|
+
</div>
|
|
2360
|
+
${this._showSources?q`
|
|
2361
|
+
<div class="fw-sc-section-body--flush">
|
|
2362
|
+
<div class="fw-sc-sources">
|
|
2363
|
+
${e.sources.map(e=>this._renderSourceRow(e))}
|
|
2364
|
+
</div>
|
|
2365
|
+
</div>
|
|
2366
|
+
`:G}
|
|
2367
|
+
</div>
|
|
2368
|
+
`:G}
|
|
2369
|
+
</div>
|
|
2370
|
+
|
|
2371
|
+
<!-- Error -->
|
|
2372
|
+
${e.error?q`
|
|
2373
|
+
<div class="fw-sc-error">
|
|
2374
|
+
<div class="fw-sc-error-title">Error</div>
|
|
2375
|
+
<div class="fw-sc-error-message">${e.error}</div>
|
|
2376
|
+
</div>
|
|
2377
|
+
`:G}
|
|
2378
|
+
${this.whipUrl||e.error?G:q`
|
|
2379
|
+
<div class="fw-sc-error" style="border-left-color: hsl(40 80% 65%)">
|
|
2380
|
+
<div class="fw-sc-error-title" style="color: hsl(40 80% 65%)">Warning</div>
|
|
2381
|
+
<div class="fw-sc-error-message">Configure WHIP endpoint to stream</div>
|
|
2382
|
+
</div>
|
|
2383
|
+
`}
|
|
2384
|
+
|
|
2385
|
+
<!-- Action Bar -->
|
|
2386
|
+
<div class="fw-sc-actions">
|
|
2387
|
+
<button
|
|
2388
|
+
type="button"
|
|
2389
|
+
class="fw-sc-action-secondary"
|
|
2390
|
+
@click=${()=>this.pc.startCamera().catch(console.error)}
|
|
2391
|
+
?disabled=${!o||a}
|
|
2392
|
+
title=${a?"Camera active":"Add Camera"}
|
|
2393
|
+
>
|
|
2394
|
+
${Se(18)}
|
|
2395
|
+
</button>
|
|
2396
|
+
<button
|
|
2397
|
+
type="button"
|
|
2398
|
+
class="fw-sc-action-secondary"
|
|
2399
|
+
@click=${()=>this.pc.startScreenShare({audio:!0}).catch(console.error)}
|
|
2400
|
+
?disabled=${!o}
|
|
2401
|
+
title="Share Screen"
|
|
2402
|
+
>
|
|
2403
|
+
${ke(18)}
|
|
2404
|
+
</button>
|
|
2405
|
+
|
|
2406
|
+
<!-- Settings -->
|
|
2407
|
+
<div style="position:relative">
|
|
2408
|
+
<button
|
|
2409
|
+
type="button"
|
|
2410
|
+
class=${ye({"fw-sc-action-secondary":!0,"fw-sc-action-secondary--active":this._showSettings})}
|
|
2411
|
+
@click=${()=>{this._showSettings=!this._showSettings}}
|
|
2412
|
+
title="Settings"
|
|
2413
|
+
>
|
|
2414
|
+
${((e=16)=>q` <svg
|
|
2415
|
+
width="${e}"
|
|
2416
|
+
height="${e}"
|
|
2417
|
+
viewBox="0 0 24 24"
|
|
2418
|
+
fill="none"
|
|
2419
|
+
stroke="currentColor"
|
|
2420
|
+
stroke-width="2"
|
|
2421
|
+
stroke-linecap="round"
|
|
2422
|
+
stroke-linejoin="round"
|
|
2423
|
+
>
|
|
2424
|
+
<circle cx="12" cy="12" r="3" />
|
|
2425
|
+
<path
|
|
2426
|
+
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"
|
|
2427
|
+
/>
|
|
2428
|
+
</svg>`)(16)}
|
|
2429
|
+
</button>
|
|
2430
|
+
${this._showSettings?this._renderSettingsPopup():G}
|
|
2431
|
+
</div>
|
|
2432
|
+
|
|
2433
|
+
<!-- Go Live / Stop -->
|
|
2434
|
+
${e.isStreaming?q`
|
|
2435
|
+
<button
|
|
2436
|
+
type="button"
|
|
2437
|
+
class="fw-sc-action-primary fw-sc-action-stop"
|
|
2438
|
+
@click=${()=>this.pc.stopStreaming().catch(console.error)}
|
|
2439
|
+
>
|
|
2440
|
+
Stop Streaming
|
|
2441
|
+
</button>
|
|
2442
|
+
`:q`
|
|
2443
|
+
<button
|
|
2444
|
+
type="button"
|
|
2445
|
+
class="fw-sc-action-primary"
|
|
2446
|
+
@click=${()=>this.pc.startStreaming().catch(console.error)}
|
|
2447
|
+
?disabled=${!n}
|
|
2448
|
+
>
|
|
2449
|
+
${"connecting"===e.state?"Connecting...":"Go Live"}
|
|
2450
|
+
</button>
|
|
2451
|
+
`}
|
|
2452
|
+
</div>
|
|
2453
|
+
</div>
|
|
2454
|
+
|
|
2455
|
+
<!-- Advanced Panel -->
|
|
2456
|
+
${this.devMode&&this._isAdvancedPanelOpen?q`
|
|
2457
|
+
<fw-sc-advanced
|
|
2458
|
+
.ic=${this.pc}
|
|
2459
|
+
@fw-close=${()=>{this._isAdvancedPanelOpen=!1}}
|
|
2460
|
+
></fw-sc-advanced>
|
|
2461
|
+
`:G}
|
|
2462
|
+
</div>
|
|
2463
|
+
`}_renderSourceRow(e){const t=this.pc.s,r=e.stream.getVideoTracks().length>0;return q`
|
|
2464
|
+
<div class=${ye({"fw-sc-source":!0})}>
|
|
2465
|
+
<div class="fw-sc-source-icon">
|
|
2466
|
+
${"camera"===e.type?Se(16):ke(16)}
|
|
2467
|
+
</div>
|
|
2468
|
+
<div class="fw-sc-source-info">
|
|
2469
|
+
<div class="fw-sc-source-label">
|
|
2470
|
+
${e.label}
|
|
2471
|
+
${e.primaryVideo&&!this.enableCompositor?q`<span class="fw-sc-primary-badge">PRIMARY</span>`:G}
|
|
2472
|
+
</div>
|
|
2473
|
+
<div class="fw-sc-source-type">${e.type}</div>
|
|
2474
|
+
</div>
|
|
2475
|
+
<div class="fw-sc-source-controls">
|
|
2476
|
+
${r&&!this.enableCompositor?q`
|
|
2477
|
+
<button
|
|
2478
|
+
type="button"
|
|
2479
|
+
class=${ye({"fw-sc-icon-btn":!0,"fw-sc-icon-btn--primary":!!e.primaryVideo})}
|
|
2480
|
+
@click=${()=>this.pc.setPrimaryVideoSource(e.id)}
|
|
2481
|
+
?disabled=${e.primaryVideo}
|
|
2482
|
+
title=${e.primaryVideo?"Primary video source":"Set as primary video"}
|
|
2483
|
+
>
|
|
2484
|
+
${$e(14)}
|
|
2485
|
+
</button>
|
|
2486
|
+
`:G}
|
|
2487
|
+
<span class="fw-sc-volume-label">${Math.round(100*e.volume)}%</span>
|
|
2488
|
+
<fw-sc-volume
|
|
2489
|
+
.value=${e.volume}
|
|
2490
|
+
@fw-sc-volume-change=${t=>this.pc.setSourceVolume(e.id,t.detail.value)}
|
|
2491
|
+
compact
|
|
2492
|
+
></fw-sc-volume>
|
|
2493
|
+
<button
|
|
2494
|
+
type="button"
|
|
2495
|
+
class=${ye({"fw-sc-icon-btn":!0,"fw-sc-icon-btn--active":e.muted})}
|
|
2496
|
+
@click=${()=>this.pc.setSourceMuted(e.id,!e.muted)}
|
|
2497
|
+
title=${e.muted?"Unmute":"Mute"}
|
|
2498
|
+
>
|
|
2499
|
+
${e.muted?((e=16)=>q` <svg
|
|
2500
|
+
width="${e}"
|
|
2501
|
+
height="${e}"
|
|
2502
|
+
viewBox="0 0 24 24"
|
|
2503
|
+
fill="none"
|
|
2504
|
+
stroke="currentColor"
|
|
2505
|
+
stroke-width="2"
|
|
2506
|
+
stroke-linecap="round"
|
|
2507
|
+
stroke-linejoin="round"
|
|
2508
|
+
>
|
|
2509
|
+
<line x1="1" y1="1" x2="23" y2="23" />
|
|
2510
|
+
<path d="M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6" />
|
|
2511
|
+
<path d="M17 16.95A7 7 0 0 1 5 12v-2m14 0v2a7 7 0 0 1-.11 1.23" />
|
|
2512
|
+
<line x1="12" y1="19" x2="12" y2="23" />
|
|
2513
|
+
<line x1="8" y1="23" x2="16" y2="23" />
|
|
2514
|
+
</svg>`)(14):((e=16)=>q` <svg
|
|
2515
|
+
width="${e}"
|
|
2516
|
+
height="${e}"
|
|
2517
|
+
viewBox="0 0 24 24"
|
|
2518
|
+
fill="none"
|
|
2519
|
+
stroke="currentColor"
|
|
2520
|
+
stroke-width="2"
|
|
2521
|
+
stroke-linecap="round"
|
|
2522
|
+
stroke-linejoin="round"
|
|
2523
|
+
>
|
|
2524
|
+
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
|
|
2525
|
+
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
|
|
2526
|
+
<line x1="12" y1="19" x2="12" y2="23" />
|
|
2527
|
+
<line x1="8" y1="23" x2="16" y2="23" />
|
|
2528
|
+
</svg>`)(14)}
|
|
2529
|
+
</button>
|
|
2530
|
+
<button
|
|
2531
|
+
type="button"
|
|
2532
|
+
class="fw-sc-icon-btn fw-sc-icon-btn--destructive"
|
|
2533
|
+
@click=${()=>this.pc.removeSource(e.id)}
|
|
2534
|
+
?disabled=${t.isStreaming}
|
|
2535
|
+
title=${t.isStreaming?"Cannot remove source while streaming":"Remove source"}
|
|
2536
|
+
>
|
|
2537
|
+
${Ce(14)}
|
|
2538
|
+
</button>
|
|
2539
|
+
</div>
|
|
2540
|
+
</div>
|
|
2541
|
+
`}_renderSettingsPopup(){const e=this.pc.s;return q`
|
|
2542
|
+
<div
|
|
2543
|
+
class="fw-sc-settings-popup"
|
|
2544
|
+
style="position:absolute;bottom:100%;left:0;margin-bottom:8px;width:192px;background:#1a1b26;border:1px solid rgba(90,96,127,0.3);box-shadow:0 4px 12px rgba(0,0,0,0.4);border-radius:4px;overflow:hidden;z-index:50"
|
|
2545
|
+
>
|
|
2546
|
+
<div style="padding:8px;border-bottom:1px solid rgba(90,96,127,0.3)">
|
|
2547
|
+
<div
|
|
2548
|
+
style="font-size:10px;color:#565f89;text-transform:uppercase;font-weight:600;margin-bottom:4px;padding-left:4px"
|
|
2549
|
+
>
|
|
2550
|
+
Quality
|
|
2551
|
+
</div>
|
|
2552
|
+
<div style="display:flex;flex-direction:column;gap:2px">
|
|
2553
|
+
${at.map(t=>q`
|
|
2554
|
+
<button
|
|
2555
|
+
type="button"
|
|
2556
|
+
@click=${()=>{e.isStreaming||(this.pc.setQualityProfile(t.id),this.devMode||(this._showSettings=!1))}}
|
|
2557
|
+
?disabled=${e.isStreaming}
|
|
2558
|
+
style="width:100%;padding:6px 8px;text-align:left;font-size:12px;border-radius:4px;border:none;cursor:${e.isStreaming?"not-allowed":"pointer"};opacity:${e.isStreaming?"0.5":"1"};background:${e.qualityProfile===t.id?"rgba(122,162,247,0.2)":"transparent"};color:${e.qualityProfile===t.id?"#7aa2f7":"#a9b1d6"}"
|
|
2559
|
+
>
|
|
2560
|
+
<div style="font-weight:500">${t.label}</div>
|
|
2561
|
+
<div style="font-size:10px;color:#565f89">${t.description}</div>
|
|
2562
|
+
</button>
|
|
2563
|
+
`)}
|
|
2564
|
+
</div>
|
|
2565
|
+
</div>
|
|
2566
|
+
${this.devMode?q`
|
|
2567
|
+
<div style="padding:8px">
|
|
2568
|
+
<div
|
|
2569
|
+
style="font-size:10px;color:#565f89;text-transform:uppercase;font-weight:600;margin-bottom:4px;padding-left:4px"
|
|
2570
|
+
>
|
|
2571
|
+
Debug
|
|
2572
|
+
</div>
|
|
2573
|
+
<div
|
|
2574
|
+
style="display:flex;flex-direction:column;gap:4px;padding-left:4px;font-size:12px;font-family:ui-monospace,monospace"
|
|
2575
|
+
>
|
|
2576
|
+
<div style="display:flex;justify-content:space-between">
|
|
2577
|
+
<span style="color:#565f89">State</span
|
|
2578
|
+
><span style="color:#c0caf5">${e.state}</span>
|
|
2579
|
+
</div>
|
|
2580
|
+
<div style="display:flex;justify-content:space-between">
|
|
2581
|
+
<span style="color:#565f89">WHIP</span
|
|
2582
|
+
><span style="color:${this.whipUrl?"#9ece6a":"#f7768e"}"
|
|
2583
|
+
>${this.whipUrl?"OK":"Not set"}</span
|
|
2584
|
+
>
|
|
2585
|
+
</div>
|
|
2586
|
+
</div>
|
|
2587
|
+
</div>
|
|
2588
|
+
`:G}
|
|
2589
|
+
</div>
|
|
2590
|
+
`}},e.FwStreamCrafter.styles=[be,xe,c`
|
|
2591
|
+
:host {
|
|
2592
|
+
display: block;
|
|
2593
|
+
}
|
|
2594
|
+
.root {
|
|
2595
|
+
display: flex;
|
|
2596
|
+
height: 100%;
|
|
2597
|
+
}
|
|
2598
|
+
.main {
|
|
2599
|
+
display: flex;
|
|
2600
|
+
flex-direction: column;
|
|
2601
|
+
flex: 1;
|
|
2602
|
+
min-width: 0;
|
|
2603
|
+
}
|
|
2604
|
+
`],r([ge({type:String,attribute:"whip-url"})],e.FwStreamCrafter.prototype,"whipUrl",void 0),r([ge({type:String,attribute:"gateway-url"})],e.FwStreamCrafter.prototype,"gatewayUrl",void 0),r([ge({type:String,attribute:"stream-key"})],e.FwStreamCrafter.prototype,"streamKey",void 0),r([ge({type:String,attribute:"initial-profile"})],e.FwStreamCrafter.prototype,"initialProfile",void 0),r([ge({type:Boolean,attribute:"auto-start-camera"})],e.FwStreamCrafter.prototype,"autoStartCamera",void 0),r([ge({type:Boolean,attribute:"dev-mode"})],e.FwStreamCrafter.prototype,"devMode",void 0),r([ge({type:Boolean})],e.FwStreamCrafter.prototype,"debug",void 0),r([ge({type:Boolean,attribute:"enable-compositor"})],e.FwStreamCrafter.prototype,"enableCompositor",void 0),r([fe()],e.FwStreamCrafter.prototype,"_showSettings",void 0),r([fe()],e.FwStreamCrafter.prototype,"_showSources",void 0),r([fe()],e.FwStreamCrafter.prototype,"_isAdvancedPanelOpen",void 0),r([me(".fw-sc-preview video")],e.FwStreamCrafter.prototype,"_videoEl",void 0),e.FwStreamCrafter=r([he("fw-streamcrafter")],e.FwStreamCrafter);const ct=[{mode:"solo",label:"Solo",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2605
|
+
<rect x="1" y="1" width="10" height="10" rx="1" />
|
|
2606
|
+
</svg>`,minSources:1},{mode:"pip-br",label:"PiP ↘",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2607
|
+
<rect x="1" y="1" width="10" height="10" rx="1" fill-opacity="0.3" />
|
|
2608
|
+
<rect x="6.5" y="6.5" width="4" height="3" rx="0.5" />
|
|
2609
|
+
</svg>`,minSources:2},{mode:"pip-bl",label:"PiP ↙",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2610
|
+
<rect x="1" y="1" width="10" height="10" rx="1" fill-opacity="0.3" />
|
|
2611
|
+
<rect x="1.5" y="6.5" width="4" height="3" rx="0.5" />
|
|
2612
|
+
</svg>`,minSources:2},{mode:"pip-tr",label:"PiP ↗",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2613
|
+
<rect x="1" y="1" width="10" height="10" rx="1" fill-opacity="0.3" />
|
|
2614
|
+
<rect x="6.5" y="2.5" width="4" height="3" rx="0.5" />
|
|
2615
|
+
</svg>`,minSources:2},{mode:"pip-tl",label:"PiP ↖",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2616
|
+
<rect x="1" y="1" width="10" height="10" rx="1" fill-opacity="0.3" />
|
|
2617
|
+
<rect x="1.5" y="2.5" width="4" height="3" rx="0.5" />
|
|
2618
|
+
</svg>`,minSources:2},{mode:"split-h",label:"Split ⬌",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2619
|
+
<rect x="1" y="1" width="4.5" height="10" rx="1" />
|
|
2620
|
+
<rect x="6.5" y="1" width="4.5" height="10" rx="1" />
|
|
2621
|
+
</svg>`,minSources:2},{mode:"split-v",label:"Split ⬍",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2622
|
+
<rect x="1" y="1" width="10" height="4.5" rx="1" />
|
|
2623
|
+
<rect x="1" y="6.5" width="10" height="4.5" rx="1" />
|
|
2624
|
+
</svg>`,minSources:2},{mode:"focus-l",label:"Focus ◀",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2625
|
+
<rect x="1" y="1" width="7" height="10" rx="1" />
|
|
2626
|
+
<rect x="8.5" y="1" width="2.5" height="10" rx="1" fill-opacity="0.5" />
|
|
2627
|
+
</svg>`,minSources:2},{mode:"focus-r",label:"Focus ▶",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2628
|
+
<rect x="1" y="1" width="2.5" height="10" rx="1" fill-opacity="0.5" />
|
|
2629
|
+
<rect x="4" y="1" width="7" height="10" rx="1" />
|
|
2630
|
+
</svg>`,minSources:2},{mode:"pip-dual-br",label:"Main+2 PiP",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2631
|
+
<rect x="1" y="1" width="10" height="10" rx="1" fill-opacity="0.3" />
|
|
2632
|
+
<rect x="7" y="4" width="3.5" height="2.5" rx="0.5" />
|
|
2633
|
+
<rect x="7" y="7" width="3.5" height="2.5" rx="0.5" />
|
|
2634
|
+
</svg>`,minSources:3},{mode:"split-pip-r",label:"Split+PiP",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2635
|
+
<rect x="1" y="1" width="4.5" height="10" rx="1" />
|
|
2636
|
+
<rect x="6.5" y="1" width="4.5" height="10" rx="1" fill-opacity="0.5" />
|
|
2637
|
+
<rect x="7.5" y="7" width="2.5" height="2.5" rx="0.5" />
|
|
2638
|
+
</svg>`,minSources:3},{mode:"featured",label:"Featured",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2639
|
+
<rect x="1" y="1" width="10" height="7.5" rx="1" />
|
|
2640
|
+
<rect x="1" y="9" width="3" height="2" rx="0.5" fill-opacity="0.5" />
|
|
2641
|
+
<rect x="4.5" y="9" width="3" height="2" rx="0.5" fill-opacity="0.5" />
|
|
2642
|
+
<rect x="8" y="9" width="3" height="2" rx="0.5" fill-opacity="0.5" />
|
|
2643
|
+
</svg>`,minSources:3},{mode:"featured-r",label:"Featured ▶",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2644
|
+
<rect x="1" y="1" width="8" height="10" rx="1" />
|
|
2645
|
+
<rect x="9.5" y="1" width="1.5" height="3" rx="0.5" fill-opacity="0.5" />
|
|
2646
|
+
<rect x="9.5" y="4.5" width="1.5" height="3" rx="0.5" fill-opacity="0.5" />
|
|
2647
|
+
<rect x="9.5" y="8" width="1.5" height="3" rx="0.5" fill-opacity="0.5" />
|
|
2648
|
+
</svg>`,minSources:3},{mode:"grid",label:"Grid",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2649
|
+
<rect x="1" y="1" width="4.5" height="4.5" rx="1" />
|
|
2650
|
+
<rect x="6.5" y="1" width="4.5" height="4.5" rx="1" />
|
|
2651
|
+
<rect x="1" y="6.5" width="4.5" height="4.5" rx="1" />
|
|
2652
|
+
<rect x="6.5" y="6.5" width="4.5" height="4.5" rx="1" />
|
|
2653
|
+
</svg>`,minSources:2},{mode:"stack",label:"Stack",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2654
|
+
<rect x="1" y="1" width="10" height="2.8" rx="0.5" />
|
|
2655
|
+
<rect x="1" y="4.6" width="10" height="2.8" rx="0.5" />
|
|
2656
|
+
<rect x="1" y="8.2" width="10" height="2.8" rx="0.5" />
|
|
2657
|
+
</svg>`,minSources:2}],lt=[{mode:"letterbox",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2658
|
+
<rect x="1" y="3" width="10" height="6" rx="1" />
|
|
2659
|
+
<rect x="0" y="1" width="12" height="1.5" fill-opacity="0.3" />
|
|
2660
|
+
<rect x="0" y="9.5" width="12" height="1.5" fill-opacity="0.3" />
|
|
2661
|
+
</svg>`,label:"Letterbox (fit)"},{mode:"crop",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2662
|
+
<rect x="0" y="0" width="12" height="12" rx="1" />
|
|
2663
|
+
<path
|
|
2664
|
+
d="M2 0v2H0v1h3V0H2zM10 0v3h2V2h-2V0H9v3h3V2h-2V0h1zM0 9v1h2v2h1V9H0zM12 9H9v3h1v-2h2v-1z"
|
|
2665
|
+
fill-opacity="0.5"
|
|
2666
|
+
/>
|
|
2667
|
+
</svg>`,label:"Crop (fill)"},{mode:"stretch",icon:()=>q`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
2668
|
+
<rect x="1" y="1" width="10" height="10" rx="1" fill-opacity="0.3" />
|
|
2669
|
+
<path
|
|
2670
|
+
d="M3 5.5h6M3 5l-1.5 1L3 7M9 5l1.5 1L9 7M5.5 3v6M5 3L6 1.5 7 3M5 9l1 1.5 1-1.5"
|
|
2671
|
+
stroke="currentColor"
|
|
2672
|
+
stroke-width="1"
|
|
2673
|
+
fill="none"
|
|
2674
|
+
/>
|
|
2675
|
+
</svg>`,label:"Stretch"}];let dt=class extends le{constructor(){super(...arguments),this._tooltipText="",this._tooltipTarget=null}render(){const e=this.ic.s.sources.length,t=ct.filter(t=>function(e,t){const r=Le.find(t=>t.mode===e);return!!r&&t>=r.minSources&&t<=r.maxSources}(t.mode,e));return q`
|
|
2676
|
+
<div class="fw-sc-layout-overlay">
|
|
2677
|
+
<div class="fw-sc-layout-bar">
|
|
2678
|
+
<div class="fw-sc-layout-section">
|
|
2679
|
+
<span class="fw-sc-layout-label">Layout</span>
|
|
2680
|
+
<div class="fw-sc-layout-icons">
|
|
2681
|
+
${t.map(e=>q`
|
|
2682
|
+
<button
|
|
2683
|
+
type="button"
|
|
2684
|
+
class="fw-sc-layout-icon"
|
|
2685
|
+
@click=${t=>{t.stopPropagation(),this._handleLayoutSelect(e.mode)}}
|
|
2686
|
+
title=${e.label}
|
|
2687
|
+
>
|
|
2688
|
+
${e.icon()}
|
|
2689
|
+
</button>
|
|
2690
|
+
`)}
|
|
2691
|
+
</div>
|
|
2692
|
+
</div>
|
|
2693
|
+
<div class="fw-sc-layout-separator"></div>
|
|
2694
|
+
<div class="fw-sc-layout-section">
|
|
2695
|
+
<span class="fw-sc-layout-label">Display</span>
|
|
2696
|
+
<div class="fw-sc-scaling-icons">
|
|
2697
|
+
${lt.map(e=>q`
|
|
2698
|
+
<button
|
|
2699
|
+
type="button"
|
|
2700
|
+
class="fw-sc-layout-icon"
|
|
2701
|
+
@click=${e=>{e.stopPropagation()}}
|
|
2702
|
+
title=${e.label}
|
|
2703
|
+
>
|
|
2704
|
+
${e.icon()}
|
|
2705
|
+
</button>
|
|
2706
|
+
`)}
|
|
2707
|
+
</div>
|
|
2708
|
+
</div>
|
|
2709
|
+
</div>
|
|
2710
|
+
</div>
|
|
2711
|
+
`}_handleLayoutSelect(e){this.dispatchEvent(new CustomEvent("fw-sc-layout-select",{detail:{mode:e},bubbles:!0,composed:!0}))}};dt.styles=[be,xe,c`
|
|
2712
|
+
:host {
|
|
2713
|
+
display: contents;
|
|
2714
|
+
}
|
|
2715
|
+
`],r([ge({attribute:!1})],dt.prototype,"ic",void 0),r([fe()],dt.prototype,"_tooltipText",void 0),r([fe()],dt.prototype,"_tooltipTarget",void 0),dt=r([he("fw-sc-compositor")],dt);let ht=class extends le{constructor(){super(...arguments),this.scenes=[],this.activeSceneId=null,this.showTransitionControls=!0,this._selectedTransition="fade",this._transitionDuration=500,this._isTransitioning=!1}render(){return q`
|
|
2716
|
+
<div class="fw-sc-scene-switcher">
|
|
2717
|
+
<div class="fw-sc-scene-switcher-header">
|
|
2718
|
+
<span class="fw-sc-scene-switcher-title">Scenes</span>
|
|
2719
|
+
${this.showTransitionControls?q`
|
|
2720
|
+
<div class="fw-sc-transition-controls">
|
|
2721
|
+
<select
|
|
2722
|
+
class="fw-sc-transition-select"
|
|
2723
|
+
.value=${this._selectedTransition}
|
|
2724
|
+
@change=${e=>{this._selectedTransition=e.target.value}}
|
|
2725
|
+
>
|
|
2726
|
+
<option value="cut">Cut</option>
|
|
2727
|
+
<option value="fade">Fade</option>
|
|
2728
|
+
<option value="slide-left">Slide Left</option>
|
|
2729
|
+
<option value="slide-right">Slide Right</option>
|
|
2730
|
+
<option value="slide-up">Slide Up</option>
|
|
2731
|
+
<option value="slide-down">Slide Down</option>
|
|
2732
|
+
</select>
|
|
2733
|
+
<input
|
|
2734
|
+
type="number"
|
|
2735
|
+
class="fw-sc-transition-duration"
|
|
2736
|
+
.value=${String(this._transitionDuration)}
|
|
2737
|
+
@change=${e=>{this._transitionDuration=Number(e.target.value)}}
|
|
2738
|
+
min="0"
|
|
2739
|
+
max="3000"
|
|
2740
|
+
step="100"
|
|
2741
|
+
title="Transition duration (ms)"
|
|
2742
|
+
/>
|
|
2743
|
+
<span class="fw-sc-transition-unit">ms</span>
|
|
2744
|
+
</div>
|
|
2745
|
+
`:G}
|
|
2746
|
+
</div>
|
|
2747
|
+
|
|
2748
|
+
<div class="fw-sc-scene-list">
|
|
2749
|
+
${this.scenes.map(e=>q`
|
|
2750
|
+
<div
|
|
2751
|
+
class=${ye({"fw-sc-scene-item":!0,"fw-sc-scene-item--active":e.id===this.activeSceneId,"fw-sc-scene-item--transitioning":this._isTransitioning})}
|
|
2752
|
+
@click=${()=>this._handleSceneClick(e.id)}
|
|
2753
|
+
style="background-color:${e.backgroundColor}"
|
|
2754
|
+
>
|
|
2755
|
+
<span class="fw-sc-scene-name">${e.name}</span>
|
|
2756
|
+
<span class="fw-sc-scene-layer-count">${e.layers.length} layers</span>
|
|
2757
|
+
${this.scenes.length>1&&e.id!==this.activeSceneId?q`
|
|
2758
|
+
<button
|
|
2759
|
+
class="fw-sc-scene-delete"
|
|
2760
|
+
@click=${t=>{t.stopPropagation(),this._handleDelete(e.id)}}
|
|
2761
|
+
title="Delete scene"
|
|
2762
|
+
>
|
|
2763
|
+
×
|
|
2764
|
+
</button>
|
|
2765
|
+
`:G}
|
|
2766
|
+
</div>
|
|
2767
|
+
`)}
|
|
2768
|
+
|
|
2769
|
+
<button
|
|
2770
|
+
class="fw-sc-scene-add"
|
|
2771
|
+
@click=${()=>this.dispatchEvent(new CustomEvent("fw-sc-scene-create",{bubbles:!0,composed:!0}))}
|
|
2772
|
+
title="Create new scene"
|
|
2773
|
+
>
|
|
2774
|
+
+
|
|
2775
|
+
</button>
|
|
2776
|
+
</div>
|
|
2777
|
+
</div>
|
|
2778
|
+
`}async _handleSceneClick(e){if(e!==this.activeSceneId&&!this._isTransitioning){this._isTransitioning=!0;try{this.dispatchEvent(new CustomEvent("fw-sc-scene-select",{detail:{sceneId:e,transition:{type:this._selectedTransition,durationMs:this._transitionDuration,easing:"ease-in-out"}},bubbles:!0,composed:!0}))}finally{this._isTransitioning=!1}}}_handleDelete(e){this.scenes.length<=1||this.dispatchEvent(new CustomEvent("fw-sc-scene-delete",{detail:{sceneId:e},bubbles:!0,composed:!0}))}};ht.styles=[be,xe,c`
|
|
2779
|
+
:host {
|
|
2780
|
+
display: block;
|
|
2781
|
+
}
|
|
2782
|
+
`],r([ge({attribute:!1})],ht.prototype,"scenes",void 0),r([ge({type:String,attribute:"active-scene-id"})],ht.prototype,"activeSceneId",void 0),r([ge({type:Boolean,attribute:"show-transition-controls"})],ht.prototype,"showTransitionControls",void 0),r([fe()],ht.prototype,"_selectedTransition",void 0),r([fe()],ht.prototype,"_transitionDuration",void 0),r([fe()],ht.prototype,"_isTransitioning",void 0),ht=r([he("fw-sc-scene-switcher")],ht);let ut=class extends le{constructor(){super(...arguments),this.layers=[],this.sources=[],this.selectedLayerId=null,this._draggedId=null,this._dragOverId=null,this._editingLayerId=null}get _sortedLayers(){return[...this.layers].sort((e,t)=>t.zIndex-e.zIndex)}_getSourceLabel(e){const t=this.sources.find(t=>t.id===e);return t?.label||e}_getSourceIcon(e){const t=this.sources.find(t=>t.id===e);switch(t?.type){case"camera":return Se(14);case"screen":return ke(14);default:return $e(14)}}render(){const e=this._sortedLayers;return q`
|
|
2783
|
+
<div class="fw-sc-layer-list">
|
|
2784
|
+
<div class="fw-sc-layer-list-header">
|
|
2785
|
+
<span class="fw-sc-layer-list-title">Layers</span>
|
|
2786
|
+
<span class="fw-sc-layer-count">${this.layers.length}</span>
|
|
2787
|
+
</div>
|
|
2788
|
+
|
|
2789
|
+
<div class="fw-sc-layer-items">
|
|
2790
|
+
${0===e.length?q` <div class="fw-sc-layer-empty">No layers. Add a source to get started.</div> `:e.map((t,r)=>q`
|
|
2791
|
+
<div
|
|
2792
|
+
class=${ye({"fw-sc-layer-item":!0,"fw-sc-layer-item--selected":t.id===this.selectedLayerId,"fw-sc-layer-item--dragging":t.id===this._draggedId,"fw-sc-layer-item--drag-over":t.id===this._dragOverId,"fw-sc-layer-item--hidden":!t.visible})}
|
|
2793
|
+
draggable="true"
|
|
2794
|
+
@dragstart=${e=>this._handleDragStart(e,t.id)}
|
|
2795
|
+
@dragover=${e=>this._handleDragOver(e,t.id)}
|
|
2796
|
+
@dragleave=${()=>{this._dragOverId=null}}
|
|
2797
|
+
@drop=${e=>this._handleDrop(e,t.id)}
|
|
2798
|
+
@dragend=${()=>{this._draggedId=null,this._dragOverId=null}}
|
|
2799
|
+
@click=${()=>this._dispatch("fw-sc-layer-select",{layerId:t.id===this.selectedLayerId?null:t.id})}
|
|
2800
|
+
>
|
|
2801
|
+
<button
|
|
2802
|
+
class=${ye({"fw-sc-layer-visibility":!0,"fw-sc-layer-visibility--visible":t.visible})}
|
|
2803
|
+
@click=${e=>{e.stopPropagation(),this._dispatch("fw-sc-visibility-toggle",{layerId:t.id,visible:!t.visible})}}
|
|
2804
|
+
title=${t.visible?"Hide layer":"Show layer"}
|
|
2805
|
+
>
|
|
2806
|
+
${t.visible?((e=14)=>q` <svg
|
|
2807
|
+
width="${e}"
|
|
2808
|
+
height="${e}"
|
|
2809
|
+
viewBox="0 0 24 24"
|
|
2810
|
+
fill="none"
|
|
2811
|
+
stroke="currentColor"
|
|
2812
|
+
stroke-width="2"
|
|
2813
|
+
stroke-linecap="round"
|
|
2814
|
+
stroke-linejoin="round"
|
|
2815
|
+
>
|
|
2816
|
+
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
|
|
2817
|
+
<circle cx="12" cy="12" r="3" />
|
|
2818
|
+
</svg>`)(14):((e=14)=>q` <svg
|
|
2819
|
+
width="${e}"
|
|
2820
|
+
height="${e}"
|
|
2821
|
+
viewBox="0 0 24 24"
|
|
2822
|
+
fill="none"
|
|
2823
|
+
stroke="currentColor"
|
|
2824
|
+
stroke-width="2"
|
|
2825
|
+
stroke-linecap="round"
|
|
2826
|
+
stroke-linejoin="round"
|
|
2827
|
+
>
|
|
2828
|
+
<path
|
|
2829
|
+
d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"
|
|
2830
|
+
/>
|
|
2831
|
+
<line x1="1" y1="1" x2="23" y2="23" />
|
|
2832
|
+
</svg>`)(14)}
|
|
2833
|
+
</button>
|
|
2834
|
+
<span class="fw-sc-layer-icon">${this._getSourceIcon(t.sourceId)}</span>
|
|
2835
|
+
<span class="fw-sc-layer-name">${this._getSourceLabel(t.sourceId)}</span>
|
|
2836
|
+
|
|
2837
|
+
${this._editingLayerId===t.id?q`
|
|
2838
|
+
<div class="fw-sc-layer-opacity">
|
|
2839
|
+
<input
|
|
2840
|
+
type="range"
|
|
2841
|
+
min="0"
|
|
2842
|
+
max="1"
|
|
2843
|
+
step="0.1"
|
|
2844
|
+
.value=${String(t.transform.opacity)}
|
|
2845
|
+
@input=${e=>this._dispatch("fw-sc-transform-edit",{layerId:t.id,transform:{opacity:Number(e.target.value)}})}
|
|
2846
|
+
@click=${e=>e.stopPropagation()}
|
|
2847
|
+
/>
|
|
2848
|
+
<span>${Math.round(100*t.transform.opacity)}%</span>
|
|
2849
|
+
</div>
|
|
2850
|
+
`:G}
|
|
2851
|
+
|
|
2852
|
+
<div class="fw-sc-layer-controls">
|
|
2853
|
+
<button
|
|
2854
|
+
class="fw-sc-layer-btn"
|
|
2855
|
+
@click=${e=>{e.stopPropagation(),this._moveUp(t.id)}}
|
|
2856
|
+
?disabled=${0===r}
|
|
2857
|
+
title="Move up"
|
|
2858
|
+
>
|
|
2859
|
+
↑
|
|
2860
|
+
</button>
|
|
2861
|
+
<button
|
|
2862
|
+
class="fw-sc-layer-btn"
|
|
2863
|
+
@click=${e=>{e.stopPropagation(),this._moveDown(t.id)}}
|
|
2864
|
+
?disabled=${r===e.length-1}
|
|
2865
|
+
title="Move down"
|
|
2866
|
+
>
|
|
2867
|
+
↓
|
|
2868
|
+
</button>
|
|
2869
|
+
<button
|
|
2870
|
+
class=${ye({"fw-sc-layer-btn":!0,"fw-sc-layer-btn--active":this._editingLayerId===t.id})}
|
|
2871
|
+
@click=${e=>{e.stopPropagation(),this._editingLayerId=this._editingLayerId===t.id?null:t.id}}
|
|
2872
|
+
title="Edit opacity"
|
|
2873
|
+
>
|
|
2874
|
+
⚙
|
|
2875
|
+
</button>
|
|
2876
|
+
<button
|
|
2877
|
+
class="fw-sc-layer-btn fw-sc-layer-btn--danger"
|
|
2878
|
+
@click=${e=>{e.stopPropagation(),this._dispatch("fw-sc-layer-remove",{layerId:t.id})}}
|
|
2879
|
+
title="Remove layer"
|
|
2880
|
+
>
|
|
2881
|
+
×
|
|
2882
|
+
</button>
|
|
2883
|
+
</div>
|
|
2884
|
+
</div>
|
|
2885
|
+
`)}
|
|
2886
|
+
</div>
|
|
2887
|
+
</div>
|
|
2888
|
+
`}_dispatch(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}_handleDragStart(e,t){this._draggedId=t,e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",t)}_handleDragOver(e,t){e.preventDefault(),e.dataTransfer.dropEffect="move",this._dragOverId=t}_handleDrop(e,t){if(e.preventDefault(),this._dragOverId=null,!this._draggedId||this._draggedId===t)return void(this._draggedId=null);const r=this._sortedLayers.map(e=>e.id),i=r.indexOf(this._draggedId),s=r.indexOf(t);if(-1===i||-1===s)return void(this._draggedId=null);const o=[...r];o.splice(i,1),o.splice(s,0,this._draggedId),this._dispatch("fw-sc-reorder",{layerIds:o}),this._draggedId=null}_moveUp(e){const t=this._sortedLayers.map(e=>e.id),r=t.indexOf(e);r<=0||([t[r-1],t[r]]=[t[r],t[r-1]],this._dispatch("fw-sc-reorder",{layerIds:t}))}_moveDown(e){const t=this._sortedLayers.map(e=>e.id),r=t.indexOf(e);r>=t.length-1||([t[r],t[r+1]]=[t[r+1],t[r]],this._dispatch("fw-sc-reorder",{layerIds:t}))}};ut.styles=[be,xe,c`
|
|
2889
|
+
:host {
|
|
2890
|
+
display: block;
|
|
2891
|
+
}
|
|
2892
|
+
`],r([ge({attribute:!1})],ut.prototype,"layers",void 0),r([ge({attribute:!1})],ut.prototype,"sources",void 0),r([ge({type:String,attribute:"selected-layer-id"})],ut.prototype,"selectedLayerId",void 0),r([fe()],ut.prototype,"_draggedId",void 0),r([fe()],ut.prototype,"_dragOverId",void 0),r([fe()],ut.prototype,"_editingLayerId",void 0),ut=r([he("fw-sc-layer-list")],ut);let pt=class extends le{constructor(){super(...arguments),this.value=1,this.min=0,this.max=2,this.snapThreshold=.05,this.compact=!1,this._isDragging=!1,this._popupPosition=0}get _displayValue(){return Math.round(100*this.value)}get _isBoost(){return this.value>1}get _isDefault(){return 1===this.value}get _accentColor(){return this._isBoost?"#e0af68":this._isDefault?"#9ece6a":"#7aa2f7"}_handleChange(e){let t=parseInt(e.target.value,10)/100;Math.abs(t-1)<=this.snapThreshold&&(t=1),this.dispatchEvent(new CustomEvent("fw-sc-volume-change",{detail:{value:t},bubbles:!0,composed:!0})),this._updatePopupPosition(t)}_handleMouseDown(){this._isDragging=!0,this._updatePopupPosition(this.value)}_handleMouseUp(){this._isDragging=!1}_updatePopupPosition(e){if(this._slider){const t=this._slider.getBoundingClientRect(),r=(e-this.min)/(this.max-this.min);this._popupPosition=r*t.width}}render(){const e=1/this.max*100+"%",t=this._isDefault?"#9ece6a":"rgba(158, 206, 106, 0.3)";return q`
|
|
2893
|
+
${this._isDragging?q`
|
|
2894
|
+
<div
|
|
2895
|
+
class="popup"
|
|
2896
|
+
style="left:${this._popupPosition}px;background:${this._accentColor}"
|
|
2897
|
+
>
|
|
2898
|
+
${this._displayValue}%${this._isDefault?" (default)":""}
|
|
2899
|
+
<div class="popup-arrow" style="border-top:6px solid ${this._accentColor}"></div>
|
|
2900
|
+
</div>
|
|
2901
|
+
`:""}
|
|
2902
|
+
<div class="track">
|
|
2903
|
+
<div class="marker" style="left:${e};background:${t}"></div>
|
|
2904
|
+
<input
|
|
2905
|
+
type="range"
|
|
2906
|
+
.min=${String(100*this.min)}
|
|
2907
|
+
.max=${String(100*this.max)}
|
|
2908
|
+
.value=${String(Math.round(100*this.value))}
|
|
2909
|
+
@input=${this._handleChange}
|
|
2910
|
+
@mousedown=${this._handleMouseDown}
|
|
2911
|
+
@mouseup=${this._handleMouseUp}
|
|
2912
|
+
@mouseleave=${this._handleMouseUp}
|
|
2913
|
+
@touchstart=${this._handleMouseDown}
|
|
2914
|
+
@touchend=${this._handleMouseUp}
|
|
2915
|
+
style="accent-color:${this._accentColor}"
|
|
2916
|
+
/>
|
|
2917
|
+
</div>
|
|
2918
|
+
`}};pt.styles=[be,c`
|
|
2919
|
+
:host {
|
|
2920
|
+
display: inline-flex;
|
|
2921
|
+
position: relative;
|
|
2922
|
+
flex: 1;
|
|
2923
|
+
}
|
|
2924
|
+
:host([compact]) {
|
|
2925
|
+
min-width: 60px;
|
|
2926
|
+
}
|
|
2927
|
+
:host(:not([compact])) {
|
|
2928
|
+
min-width: 100px;
|
|
2929
|
+
}
|
|
2930
|
+
.track {
|
|
2931
|
+
position: relative;
|
|
2932
|
+
width: 100%;
|
|
2933
|
+
}
|
|
2934
|
+
.marker {
|
|
2935
|
+
position: absolute;
|
|
2936
|
+
top: 0;
|
|
2937
|
+
bottom: 0;
|
|
2938
|
+
width: 2px;
|
|
2939
|
+
border-radius: 1px;
|
|
2940
|
+
z-index: 1;
|
|
2941
|
+
pointer-events: none;
|
|
2942
|
+
transform: translateX(-50%);
|
|
2943
|
+
}
|
|
2944
|
+
input[type="range"] {
|
|
2945
|
+
width: 100%;
|
|
2946
|
+
height: 6px;
|
|
2947
|
+
border-radius: 3px;
|
|
2948
|
+
cursor: pointer;
|
|
2949
|
+
}
|
|
2950
|
+
.popup {
|
|
2951
|
+
position: absolute;
|
|
2952
|
+
bottom: 100%;
|
|
2953
|
+
transform: translateX(-50%);
|
|
2954
|
+
margin-bottom: 8px;
|
|
2955
|
+
padding: 4px 8px;
|
|
2956
|
+
color: #1a1b26;
|
|
2957
|
+
border-radius: 4px;
|
|
2958
|
+
font-size: 12px;
|
|
2959
|
+
font-weight: 600;
|
|
2960
|
+
font-family: monospace;
|
|
2961
|
+
white-space: nowrap;
|
|
2962
|
+
pointer-events: none;
|
|
2963
|
+
z-index: 100;
|
|
2964
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
2965
|
+
}
|
|
2966
|
+
.popup-arrow {
|
|
2967
|
+
position: absolute;
|
|
2968
|
+
top: 100%;
|
|
2969
|
+
left: 50%;
|
|
2970
|
+
transform: translateX(-50%);
|
|
2971
|
+
width: 0;
|
|
2972
|
+
height: 0;
|
|
2973
|
+
border-left: 6px solid transparent;
|
|
2974
|
+
border-right: 6px solid transparent;
|
|
2975
|
+
}
|
|
2976
|
+
`],r([ge({type:Number})],pt.prototype,"value",void 0),r([ge({type:Number})],pt.prototype,"min",void 0),r([ge({type:Number})],pt.prototype,"max",void 0),r([ge({type:Number,attribute:"snap-threshold"})],pt.prototype,"snapThreshold",void 0),r([ge({type:Boolean})],pt.prototype,"compact",void 0),r([fe()],pt.prototype,"_isDragging",void 0),r([fe()],pt.prototype,"_popupPosition",void 0),r([me("input[type=range]")],pt.prototype,"_slider",void 0),pt=r([he("fw-sc-volume")],pt);let gt=class extends le{constructor(){super(...arguments),this._activeTab="stats"}render(){return q`
|
|
2977
|
+
<div class="panel">
|
|
2978
|
+
<div class="header">
|
|
2979
|
+
<div class="tabs">
|
|
2980
|
+
<button
|
|
2981
|
+
class=${ye({tab:!0,"tab--active":"stats"===this._activeTab})}
|
|
2982
|
+
@click=${()=>{this._activeTab="stats"}}
|
|
2983
|
+
>
|
|
2984
|
+
Stats
|
|
2985
|
+
</button>
|
|
2986
|
+
<button
|
|
2987
|
+
class=${ye({tab:!0,"tab--active":"info"===this._activeTab})}
|
|
2988
|
+
@click=${()=>{this._activeTab="info"}}
|
|
2989
|
+
>
|
|
2990
|
+
Info
|
|
2991
|
+
</button>
|
|
2992
|
+
</div>
|
|
2993
|
+
<button
|
|
2994
|
+
class="close"
|
|
2995
|
+
@click=${()=>this.dispatchEvent(new CustomEvent("fw-close",{bubbles:!0,composed:!0}))}
|
|
2996
|
+
aria-label="Close panel"
|
|
2997
|
+
>
|
|
2998
|
+
${Ce(14)}
|
|
2999
|
+
</button>
|
|
3000
|
+
</div>
|
|
3001
|
+
<div class="body">
|
|
3002
|
+
${"stats"===this._activeTab?this._renderStats():this._renderInfo()}
|
|
3003
|
+
</div>
|
|
3004
|
+
</div>
|
|
3005
|
+
`}_renderStats(){const e=this.ic.s,t=e.stats;return q`
|
|
3006
|
+
<div class="section">
|
|
3007
|
+
<div class="label">Connection</div>
|
|
3008
|
+
${this._row("State",e.state)}
|
|
3009
|
+
${this._row("WebCodecs",e.isWebCodecsActive?"Active":e.useWebCodecs?"Pending":"Off")}
|
|
3010
|
+
</div>
|
|
3011
|
+
${t?q`
|
|
3012
|
+
<div class="section">
|
|
3013
|
+
<div class="label">WebRTC Stats</div>
|
|
3014
|
+
${this._row("Video bitrate",t.video.bitrate?`${Math.round(t.video.bitrate/1e3)} kbps`:"—")}
|
|
3015
|
+
${this._row("Audio bitrate",t.audio.bitrate?`${Math.round(t.audio.bitrate/1e3)} kbps`:"—")}
|
|
3016
|
+
${this._row("RTT",t.connection.rtt?`${(1e3*t.connection.rtt).toFixed(0)} ms`:"—")}
|
|
3017
|
+
${this._row("FPS",t.video.framesPerSecond?String(t.video.framesPerSecond):"—")}
|
|
3018
|
+
${this._row("Packets sent",String(t.video.packetsSent))}
|
|
3019
|
+
${this._row("Packets lost",String(t.video.packetsLost))}
|
|
3020
|
+
</div>
|
|
3021
|
+
`:G}
|
|
3022
|
+
${e.encoderStats?q`
|
|
3023
|
+
<div class="section">
|
|
3024
|
+
<div class="label">Encoder</div>
|
|
3025
|
+
${this._row("Video frames",String(e.encoderStats.video.framesEncoded))}
|
|
3026
|
+
${this._row("Video pending",String(e.encoderStats.video.framesPending))}
|
|
3027
|
+
${this._row("Audio samples",String(e.encoderStats.audio.samplesEncoded))}
|
|
3028
|
+
</div>
|
|
3029
|
+
`:G}
|
|
3030
|
+
`}_renderInfo(){const e=this.ic.s;return q`
|
|
3031
|
+
<div class="section">
|
|
3032
|
+
<div class="label">Configuration</div>
|
|
3033
|
+
${this._row("Profile",e.qualityProfile)} ${this._row("Sources",String(e.sources.length))}
|
|
3034
|
+
${this._row("WebCodecs Available",e.isWebCodecsAvailable?"Yes":"No")}
|
|
3035
|
+
</div>
|
|
3036
|
+
<div class="section">
|
|
3037
|
+
<div class="label">Sources</div>
|
|
3038
|
+
${e.sources.map(e=>q`
|
|
3039
|
+
${this._row(e.label,`${e.type} ${e.muted?"(muted)":""}`)}
|
|
3040
|
+
`)}
|
|
3041
|
+
</div>
|
|
3042
|
+
`}_row(e,t){return q`<div class="row">
|
|
3043
|
+
<span class="row-label">${e}</span><span class="row-value">${t}</span>
|
|
3044
|
+
</div>`}};function ft(e,t){customElements.get(e)||customElements.define(e,t)}return gt.styles=[be,xe,c`
|
|
3045
|
+
:host {
|
|
3046
|
+
display: block;
|
|
3047
|
+
}
|
|
3048
|
+
.panel {
|
|
3049
|
+
width: 320px;
|
|
3050
|
+
height: 100%;
|
|
3051
|
+
border-left: 1px solid rgba(90, 96, 127, 0.3);
|
|
3052
|
+
background: #1a1b26;
|
|
3053
|
+
overflow: auto;
|
|
3054
|
+
font-size: 0.75rem;
|
|
3055
|
+
color: #a9b1d6;
|
|
3056
|
+
}
|
|
3057
|
+
.header {
|
|
3058
|
+
display: flex;
|
|
3059
|
+
align-items: center;
|
|
3060
|
+
justify-content: space-between;
|
|
3061
|
+
padding: 0.5rem 0.75rem;
|
|
3062
|
+
border-bottom: 1px solid rgba(90, 96, 127, 0.3);
|
|
3063
|
+
}
|
|
3064
|
+
.tabs {
|
|
3065
|
+
display: flex;
|
|
3066
|
+
gap: 0.5rem;
|
|
3067
|
+
}
|
|
3068
|
+
.tab {
|
|
3069
|
+
padding: 0.25rem 0.5rem;
|
|
3070
|
+
border: none;
|
|
3071
|
+
background: none;
|
|
3072
|
+
color: #565f89;
|
|
3073
|
+
font-size: 0.6875rem;
|
|
3074
|
+
font-weight: 600;
|
|
3075
|
+
cursor: pointer;
|
|
3076
|
+
border-radius: 0.25rem;
|
|
3077
|
+
}
|
|
3078
|
+
.tab--active {
|
|
3079
|
+
color: #c0caf5;
|
|
3080
|
+
background: rgba(90, 96, 127, 0.2);
|
|
3081
|
+
}
|
|
3082
|
+
.close {
|
|
3083
|
+
display: flex;
|
|
3084
|
+
background: none;
|
|
3085
|
+
border: none;
|
|
3086
|
+
color: #565f89;
|
|
3087
|
+
cursor: pointer;
|
|
3088
|
+
padding: 0;
|
|
3089
|
+
}
|
|
3090
|
+
.close:hover {
|
|
3091
|
+
color: #c0caf5;
|
|
3092
|
+
}
|
|
3093
|
+
.body {
|
|
3094
|
+
padding: 0.75rem;
|
|
3095
|
+
}
|
|
3096
|
+
.section {
|
|
3097
|
+
margin-bottom: 0.75rem;
|
|
3098
|
+
}
|
|
3099
|
+
.label {
|
|
3100
|
+
font-size: 0.625rem;
|
|
3101
|
+
font-weight: 600;
|
|
3102
|
+
text-transform: uppercase;
|
|
3103
|
+
letter-spacing: 0.05em;
|
|
3104
|
+
color: #565f89;
|
|
3105
|
+
margin-bottom: 0.375rem;
|
|
3106
|
+
}
|
|
3107
|
+
.row {
|
|
3108
|
+
display: flex;
|
|
3109
|
+
justify-content: space-between;
|
|
3110
|
+
padding: 0.125rem 0;
|
|
3111
|
+
}
|
|
3112
|
+
.row-label {
|
|
3113
|
+
color: #565f89;
|
|
3114
|
+
}
|
|
3115
|
+
.row-value {
|
|
3116
|
+
color: #c0caf5;
|
|
3117
|
+
font-family: ui-monospace, monospace;
|
|
3118
|
+
font-variant-numeric: tabular-nums;
|
|
3119
|
+
}
|
|
3120
|
+
`],r([ge({attribute:!1})],gt.prototype,"ic",void 0),r([fe()],gt.prototype,"_activeTab",void 0),gt=r([he("fw-sc-advanced")],gt),ft("fw-streamcrafter",e.FwStreamCrafter),ft("fw-sc-compositor",dt),ft("fw-sc-scene-switcher",ht),ft("fw-sc-layer-list",ut),ft("fw-sc-volume",pt),ft("fw-sc-advanced",gt),e.IngestControllerHost=nt,e}({});
|
|
3121
|
+
//# sourceMappingURL=fw-streamcrafter.iife.js.map
|