@signalwire/web-components 1.0.0-dev-20260508182243 → 1.0.0-dev-20260511184148

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.
@@ -41,22 +41,13 @@ export declare class SwCallMedia extends LitElement {
41
41
  private _remoteStreamValue;
42
42
  private _lastTrackSignature;
43
43
  private _subscriptions;
44
- private _resizeObserver?;
45
- private _videoResizeHandler?;
46
- private _windowResizeHandler?;
47
- private _videoElement?;
48
- private _paddingWrapper?;
49
44
  connectedCallback(): void;
50
45
  protected updated(changedProperties: Map<string, unknown>): void;
51
46
  disconnectedCallback(): void;
52
- protected firstUpdated(): void;
53
47
  private _setupDirectSubscriptions;
54
48
  private _computeTrackSignature;
55
49
  private _cleanupDirectSubscriptions;
56
50
  private _applySinkId;
57
- private _setupResizeObserver;
58
- private _recalculateDimensions;
59
- private _cleanupResizeObserver;
60
51
  private _cleanupVideoElement;
61
52
  render(): import("lit-html").TemplateResult<1>;
62
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sw-call-media.d.ts","sourceRoot":"","sources":["../../src/components/sw-call-media.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAI5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAS9C,qBACa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,MAAM,0BAyDX;IAEF;;;OAGG;IACyB,IAAI,CAAC,EAAE,IAAI,CAAC;IAExC;;;OAGG;IAC6B,MAAM,EAAE,WAAW,GAAG,IAAI,CAAQ;IAIlE,OAAO,CAAC,UAAU,CAAC,CAAY;IAI/B,OAAO,CAAC,aAAa,CAAC,CAAe;IAErC,OAAO,CAAC,kBAAkB,CAA4B;IACtD,OAAO,CAAC,mBAAmB,CAAM;IACjC,OAAO,CAAC,cAAc,CAAsB;IAC5C,OAAO,CAAC,eAAe,CAAC,CAAiB;IACzC,OAAO,CAAC,mBAAmB,CAAC,CAAa;IACzC,OAAO,CAAC,oBAAoB,CAAC,CAAa;IAC1C,OAAO,CAAC,aAAa,CAAC,CAAmB;IACzC,OAAO,CAAC,eAAe,CAAC,CAAiB;IAIzC,iBAAiB;IASjB,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IA4ChE,oBAAoB;IAOpB,SAAS,CAAC,YAAY,IAAI,IAAI;IAW9B,OAAO,CAAC,yBAAyB;IAiBjC,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,oBAAoB;IAwB5B,OAAO,CAAC,sBAAsB;IA6D9B,OAAO,CAAC,sBAAsB;IAgB9B,OAAO,CAAC,oBAAoB;IAO5B,MAAM;CAoBP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,eAAe,EAAE,WAAW,CAAC;KAC9B;CACF"}
1
+ {"version":3,"file":"sw-call-media.d.ts","sourceRoot":"","sources":["../../src/components/sw-call-media.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAI5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAQ9C,qBACa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,MAAM,0BAsDX;IAEF;;;OAGG;IACyB,IAAI,CAAC,EAAE,IAAI,CAAC;IAExC;;;OAGG;IAC6B,MAAM,EAAE,WAAW,GAAG,IAAI,CAAQ;IAIlE,OAAO,CAAC,UAAU,CAAC,CAAY;IAI/B,OAAO,CAAC,aAAa,CAAC,CAAe;IAErC,OAAO,CAAC,kBAAkB,CAA4B;IACtD,OAAO,CAAC,mBAAmB,CAAM;IACjC,OAAO,CAAC,cAAc,CAAsB;IAI5C,iBAAiB;IASjB,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IA4ChE,oBAAoB;IAQpB,OAAO,CAAC,yBAAyB;IAiBjC,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,oBAAoB;IAO5B,MAAM;CAqBP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,eAAe,EAAE,WAAW,CAAC;KAC9B;CACF"}
@@ -1,18 +1,17 @@
1
- import { LitElement as V, html as q, css as E } from "lit";
2
- import { property as b, state as g, customElement as M } from "lit/decorators.js";
3
- import { consume as w } from "@lit/context";
4
- import { callStateContext as W } from "../context/call-state-context.js";
5
- import { devicesContext as $ } from "../context/devices-context.js";
6
- import { debounce as j } from "../utils/debounce.js";
7
- import { attachMediaStream as _, waitForVideoReady as L, detachMediaStream as I } from "../utils/video.js";
8
- import { getLogger as A } from "@signalwire/js";
9
- var B = Object.defineProperty, X = Object.getOwnPropertyDescriptor, d = (t, e, s, o) => {
10
- for (var i = o > 1 ? void 0 : o ? X(e, s) : e, a = t.length - 1, r; a >= 0; a--)
11
- (r = t[a]) && (i = (o ? r(e, s, i) : r(i)) || i);
12
- return o && i && B(e, s, i), i;
1
+ import { LitElement as v, html as S, css as _ } from "lit";
2
+ import { property as d, state as p, customElement as b } from "lit/decorators.js";
3
+ import { consume as m } from "@lit/context";
4
+ import { callStateContext as f } from "../context/call-state-context.js";
5
+ import { devicesContext as g } from "../context/devices-context.js";
6
+ import { attachMediaStream as h, detachMediaStream as w } from "../utils/video.js";
7
+ import { getLogger as y } from "@signalwire/js";
8
+ var k = Object.defineProperty, T = Object.getOwnPropertyDescriptor, u = (t, e, s, r) => {
9
+ for (var a = r > 1 ? void 0 : r ? T(e, s) : e, c = t.length - 1, i; c >= 0; c--)
10
+ (i = t[c]) && (a = (r ? i(e, s, a) : i(a)) || a);
11
+ return r && a && k(e, s, a), a;
13
12
  };
14
- const Y = A();
15
- let l = class extends V {
13
+ const V = y();
14
+ let o = class extends v {
16
15
  constructor() {
17
16
  super(...arguments), this.stream = null, this._remoteStreamValue = null, this._lastTrackSignature = "", this._subscriptions = [];
18
17
  }
@@ -21,47 +20,40 @@ let l = class extends V {
21
20
  super.connectedCallback(), !this.stream && this.call && this._setupDirectSubscriptions(), this.stream && (this._remoteStreamValue = this.stream, this._lastTrackSignature = this._computeTrackSignature(this.stream));
22
21
  }
23
22
  updated(t) {
24
- var e, s, o, i, a;
23
+ var e, s, r, a, c;
25
24
  if (super.updated(t), t.has("call") && (this._cleanupDirectSubscriptions(), !this.stream && this.call && this._setupDirectSubscriptions()), t.has("stream")) {
26
25
  this._cleanupDirectSubscriptions();
27
- const r = this.stream, n = this._computeTrackSignature(r);
28
- if (r !== this._remoteStreamValue || n !== this._lastTrackSignature) {
29
- this._remoteStreamValue = r, this._lastTrackSignature = n;
30
- const c = (e = this.shadowRoot) == null ? void 0 : e.querySelector("video.mcu-video");
31
- c && _(c, r);
26
+ const i = this.stream, l = this._computeTrackSignature(i);
27
+ if (i !== this._remoteStreamValue || l !== this._lastTrackSignature) {
28
+ this._remoteStreamValue = i, this._lastTrackSignature = l;
29
+ const n = (e = this.shadowRoot) == null ? void 0 : e.querySelector("video.mcu-video");
30
+ n && h(n, i);
32
31
  }
33
32
  }
34
33
  if (!this.stream && !this.call && t.has("_callState")) {
35
- const r = ((s = this._callState) == null ? void 0 : s.remoteStream) ?? null, n = this._computeTrackSignature(r);
36
- if (r !== this._remoteStreamValue || n !== this._lastTrackSignature) {
37
- this._remoteStreamValue = r, this._lastTrackSignature = n;
38
- const c = (o = this.shadowRoot) == null ? void 0 : o.querySelector("video.mcu-video");
39
- c && _(c, r);
34
+ const i = ((s = this._callState) == null ? void 0 : s.remoteStream) ?? null, l = this._computeTrackSignature(i);
35
+ if (i !== this._remoteStreamValue || l !== this._lastTrackSignature) {
36
+ this._remoteStreamValue = i, this._lastTrackSignature = l;
37
+ const n = (r = this.shadowRoot) == null ? void 0 : r.querySelector("video.mcu-video");
38
+ n && h(n, i);
40
39
  }
41
40
  }
42
- t.has("_devicesState") && this._applySinkId(((a = (i = this._devicesState) == null ? void 0 : i.selectedAudioOutput) == null ? void 0 : a.deviceId) ?? "");
41
+ t.has("_devicesState") && this._applySinkId(((c = (a = this._devicesState) == null ? void 0 : a.selectedAudioOutput) == null ? void 0 : c.deviceId) ?? "");
43
42
  }
44
43
  disconnectedCallback() {
45
- super.disconnectedCallback(), this._cleanupDirectSubscriptions(), this._cleanupResizeObserver(), this._cleanupVideoElement();
46
- }
47
- firstUpdated() {
48
- var e;
49
- const t = (e = this.shadowRoot) == null ? void 0 : e.querySelector("video.mcu-video");
50
- t && L(t).then(() => {
51
- this.isConnected && this._setupResizeObserver();
52
- });
44
+ super.disconnectedCallback(), this._cleanupDirectSubscriptions(), this._cleanupVideoElement();
53
45
  }
54
46
  // ── Direct call subscriptions (legacy / standalone) ────────────────
55
47
  _setupDirectSubscriptions() {
56
48
  this.call && this._subscriptions.push(
57
49
  this.call.remoteStream$.subscribe((t) => {
58
- var o;
50
+ var r;
59
51
  const e = this._computeTrackSignature(t);
60
52
  if (t === this._remoteStreamValue && e === this._lastTrackSignature)
61
53
  return;
62
54
  this._remoteStreamValue = t, this._lastTrackSignature = e, this.requestUpdate();
63
- const s = (o = this.shadowRoot) == null ? void 0 : o.querySelector("video.mcu-video");
64
- s && _(s, t);
55
+ const s = (r = this.shadowRoot) == null ? void 0 : r.querySelector("video.mcu-video");
56
+ s && h(s, t);
65
57
  })
66
58
  );
67
59
  }
@@ -75,51 +67,19 @@ let l = class extends V {
75
67
  _applySinkId(t) {
76
68
  var s;
77
69
  const e = (s = this.shadowRoot) == null ? void 0 : s.querySelector("video.mcu-video");
78
- e != null && e.setSinkId && e.setSinkId(t).catch((o) => {
79
- Y.error("[SwCallMedia] Failed to set audio output device:", o);
70
+ e != null && e.setSinkId && e.setSinkId(t).catch((r) => {
71
+ V.error("[SwCallMedia] Failed to set audio output device:", r);
80
72
  });
81
73
  }
82
- // ── Resize / dimension management ──────────────────────────────────
83
- _setupResizeObserver() {
84
- var s, o;
85
- const t = (s = this.shadowRoot) == null ? void 0 : s.querySelector("video.mcu-video"), e = (o = this.shadowRoot) == null ? void 0 : o.querySelector(".padding-wrapper");
86
- !t || !e || (this._videoElement = t, this._paddingWrapper = e, this._resizeObserver = new ResizeObserver(
87
- j(() => this._recalculateDimensions(), 50)
88
- ), this._resizeObserver.observe(this), this._videoResizeHandler = () => this._recalculateDimensions(), t.addEventListener("resize", this._videoResizeHandler), this._windowResizeHandler = () => {
89
- requestAnimationFrame(() => this._recalculateDimensions());
90
- }, window.addEventListener("resize", this._windowResizeHandler), this._recalculateDimensions());
91
- }
92
- _recalculateDimensions() {
93
- const t = this._videoElement, e = this._paddingWrapper;
94
- if (!t || !e) return;
95
- if (!t.videoWidth || !t.videoHeight) {
96
- e.style.width = "100%", e.style.height = "100%", e.style.paddingBottom = "0", e.style.transform = "none";
97
- return;
98
- }
99
- const s = window.innerWidth, o = window.innerHeight, i = this.getBoundingClientRect(), a = i.width, r = i.height;
100
- if (a <= 0 || r <= 0) return;
101
- const n = Math.min(1, (s - i.left) / a), c = Math.min(1, (o - i.top) / r), f = Math.min(n, c), p = a * f, m = r * f;
102
- if (p <= 0 || m <= 0) return;
103
- const v = t.videoWidth / t.videoHeight, S = p / m;
104
- let h, u;
105
- v > S ? (h = p, u = h / v) : (u = m, h = u * v);
106
- const y = Math.max(0, i.left), R = Math.max(0, i.top), z = Math.min(s, i.right), k = Math.min(o, i.bottom), x = (y + z) / 2, O = (R + k) / 2, H = i.left + a / 2, C = i.top + r / 2, T = x - H, D = O - C;
107
- e.style.width = `${h}px`, e.style.height = `${u}px`, e.style.paddingBottom = "0", e.style.transform = `translate(${T}px, ${D}px)`;
108
- }
109
- _cleanupResizeObserver() {
110
- var e;
111
- this._resizeObserver && (this._resizeObserver.disconnect(), this._resizeObserver = void 0);
112
- const t = (e = this.shadowRoot) == null ? void 0 : e.querySelector("video.mcu-video");
113
- t && this._videoResizeHandler && (t.removeEventListener("resize", this._videoResizeHandler), this._videoResizeHandler = void 0), this._windowResizeHandler && (window.removeEventListener("resize", this._windowResizeHandler), this._windowResizeHandler = void 0);
114
- }
74
+ // ── Cleanup ─────────────────────────────────────────────────────────
115
75
  _cleanupVideoElement() {
116
76
  var e;
117
77
  const t = (e = this.shadowRoot) == null ? void 0 : e.querySelector("video.mcu-video");
118
- t && I(t);
78
+ t && w(t);
119
79
  }
120
80
  // ── Render ─────────────────────────────────────────────────────────
121
81
  render() {
122
- return q`
82
+ return S`
123
83
  <div class="mcu-content" part="container">
124
84
  <div class="padding-wrapper">
125
85
  <div class="mcu-wrapper">
@@ -128,6 +88,7 @@ let l = class extends V {
128
88
  part="video"
129
89
  autoplay
130
90
  playsinline
91
+ muted
131
92
  .srcObject=${this._remoteStreamValue}
132
93
  ></video>
133
94
  </div>
@@ -139,7 +100,7 @@ let l = class extends V {
139
100
  `;
140
101
  }
141
102
  };
142
- l.styles = E`
103
+ o.styles = _`
143
104
  :host {
144
105
  display: block;
145
106
  width: 100%;
@@ -159,13 +120,13 @@ l.styles = E`
159
120
  overflow: hidden;
160
121
  }
161
122
 
123
+ /* Fill the parent box. The video element below uses object-fit: contain
124
+ so the stream letterboxes into whatever rectangle the parent gives us
125
+ — no JS sizing pass required. */
162
126
  .padding-wrapper {
163
127
  position: relative;
164
- max-width: 100%;
165
- max-height: 100%;
166
- transition:
167
- width 0.3s ease,
168
- height 0.3s ease;
128
+ width: 100%;
129
+ height: 100%;
169
130
  }
170
131
 
171
132
  /* Outer rounding is owned by sw-ui-call-layout's :host border-radius +
@@ -174,10 +135,7 @@ l.styles = E`
174
135
  (page / modal backdrop) as a black sliver in light mode. */
175
136
  .mcu-wrapper {
176
137
  position: absolute;
177
- top: 0;
178
- left: 0;
179
- right: 0;
180
- bottom: 0;
138
+ inset: 0;
181
139
  overflow: hidden;
182
140
  }
183
141
 
@@ -197,24 +155,24 @@ l.styles = E`
197
155
  pointer-events: none;
198
156
  }
199
157
  `;
200
- d([
201
- b({ type: Object })
202
- ], l.prototype, "call", 2);
203
- d([
204
- b({ attribute: !1 })
205
- ], l.prototype, "stream", 2);
206
- d([
207
- w({ context: W, subscribe: !0 }),
208
- g()
209
- ], l.prototype, "_callState", 2);
210
- d([
211
- w({ context: $, subscribe: !0 }),
212
- g()
213
- ], l.prototype, "_devicesState", 2);
214
- l = d([
215
- M("sw-call-media")
216
- ], l);
158
+ u([
159
+ d({ type: Object })
160
+ ], o.prototype, "call", 2);
161
+ u([
162
+ d({ attribute: !1 })
163
+ ], o.prototype, "stream", 2);
164
+ u([
165
+ m({ context: f, subscribe: !0 }),
166
+ p()
167
+ ], o.prototype, "_callState", 2);
168
+ u([
169
+ m({ context: g, subscribe: !0 }),
170
+ p()
171
+ ], o.prototype, "_devicesState", 2);
172
+ o = u([
173
+ b("sw-call-media")
174
+ ], o);
217
175
  export {
218
- l as SwCallMedia
176
+ o as SwCallMedia
219
177
  };
220
178
  //# sourceMappingURL=sw-call-media.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sw-call-media.js","sources":["../../src/components/sw-call-media.ts"],"sourcesContent":["/**\n * Call Media Component\n *\n * Renders the remote video stream with aspect-ratio-aware sizing.\n *\n * Input precedence (most specific wins): `.stream` > `.call` > context.\n *\n * @example\n * ```html\n * <!-- Inside a context provider (sw-call-widget): -->\n * <sw-call-media></sw-call-media>\n *\n * <!-- With an explicit Call: -->\n * <sw-call-media .call=${call}></sw-call-media>\n *\n * <!-- With a raw remote stream: -->\n * <sw-call-media .stream=${remoteStream}></sw-call-media>\n * ```\n *\n * @csspart container - Outer container that holds the video and overlay layers.\n * @csspart video - The `<video>` element rendering the remote stream.\n *\n * @slot - Default slot for overlay layers (e.g. `<sw-self-media>`, `<sw-participants>`).\n */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { consume } from '@lit/context';\nimport { Subscription } from 'rxjs';\nimport type { Call } from '../types/index.js';\nimport { callStateContext, type CallState } from '../context/call-state-context.js';\nimport { devicesContext, type DevicesState } from '../context/devices-context.js';\nimport { debounce } from '../utils/debounce.js';\nimport { waitForVideoReady, attachMediaStream, detachMediaStream } from '../utils/video.js';\nimport { getLogger } from '@signalwire/js';\n\nconst logger = getLogger();\n\n@customElement('sw-call-media')\nexport class SwCallMedia extends LitElement {\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n overflow: hidden;\n }\n\n .mcu-content {\n position: relative;\n width: 100%;\n height: 100%;\n margin: 0 auto;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: var(--bg-page, #0e0e18);\n overflow: hidden;\n }\n\n .padding-wrapper {\n position: relative;\n max-width: 100%;\n max-height: 100%;\n transition:\n width 0.3s ease,\n height 0.3s ease;\n }\n\n /* Outer rounding is owned by sw-ui-call-layout's :host border-radius +\n overflow:hidden. Rounding here too carves a notch out of the right\n edge of the video cell that exposes whatever sits behind the widget\n (page / modal backdrop) as a black sliver in light mode. */\n .mcu-wrapper {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n overflow: hidden;\n }\n\n .mcu-video {\n width: 100%;\n height: 100%;\n display: block;\n object-fit: contain;\n }\n\n .mcu-layers {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n }\n `;\n\n /**\n * Explicit Call — when set, subscribes directly to its observables instead\n * of relying on context. Overridden by `.stream` if both are set.\n */\n @property({ type: Object }) call?: Call;\n\n /**\n * Explicit remote MediaStream — highest precedence. Bypasses both `.call`\n * and context. Useful for raw rendering with no SDK at all.\n */\n @property({ attribute: false }) stream: MediaStream | null = null;\n\n @consume({ context: callStateContext, subscribe: true })\n @state()\n private _callState?: CallState;\n\n @consume({ context: devicesContext, subscribe: true })\n @state()\n private _devicesState?: DevicesState;\n\n private _remoteStreamValue: MediaStream | null = null;\n private _lastTrackSignature = '';\n private _subscriptions: Subscription[] = [];\n private _resizeObserver?: ResizeObserver;\n private _videoResizeHandler?: () => void;\n private _windowResizeHandler?: () => void;\n private _videoElement?: HTMLVideoElement;\n private _paddingWrapper?: HTMLDivElement;\n\n // ── Lifecycle ──────────────────────────────────────────────────────\n\n connectedCallback() {\n super.connectedCallback();\n if (!this.stream && this.call) this._setupDirectSubscriptions();\n if (this.stream) {\n this._remoteStreamValue = this.stream;\n this._lastTrackSignature = this._computeTrackSignature(this.stream);\n }\n }\n\n protected updated(changedProperties: Map<string, unknown>): void {\n super.updated(changedProperties);\n\n // Direct call prop changed — re-subscribe (only meaningful when `.stream` isn't set)\n if (changedProperties.has('call')) {\n this._cleanupDirectSubscriptions();\n if (!this.stream && this.call) this._setupDirectSubscriptions();\n }\n\n // Explicit `.stream` prop wins — attach it and skip everything else.\n if (changedProperties.has('stream')) {\n this._cleanupDirectSubscriptions();\n const stream = this.stream;\n const signature = this._computeTrackSignature(stream);\n if (stream !== this._remoteStreamValue || signature !== this._lastTrackSignature) {\n this._remoteStreamValue = stream;\n this._lastTrackSignature = signature;\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement;\n if (video) attachMediaStream(video, stream);\n }\n }\n\n // Context-driven: stream or its track set changed.\n // WebRTC delivers tracks one at a time via ontrack, and the SDK re-emits\n // the same MediaStream reference each time — so we must also re-attach when\n // the track set changes, otherwise Chromium may never render a video track\n // added after the initial srcObject assignment.\n if (!this.stream && !this.call && changedProperties.has('_callState')) {\n const stream = this._callState?.remoteStream ?? null;\n const signature = this._computeTrackSignature(stream);\n if (stream !== this._remoteStreamValue || signature !== this._lastTrackSignature) {\n this._remoteStreamValue = stream;\n this._lastTrackSignature = signature;\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement;\n if (video) attachMediaStream(video, stream);\n }\n }\n\n // Audio output device changed\n if (changedProperties.has('_devicesState')) {\n this._applySinkId(this._devicesState?.selectedAudioOutput?.deviceId ?? '');\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this._cleanupDirectSubscriptions();\n this._cleanupResizeObserver();\n this._cleanupVideoElement();\n }\n\n protected firstUpdated(): void {\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement;\n if (video) {\n waitForVideoReady(video).then(() => {\n if (this.isConnected) this._setupResizeObserver();\n });\n }\n }\n\n // ── Direct call subscriptions (legacy / standalone) ────────────────\n\n private _setupDirectSubscriptions(): void {\n if (!this.call) return;\n this._subscriptions.push(\n this.call.remoteStream$.subscribe((stream: MediaStream | null) => {\n const signature = this._computeTrackSignature(stream);\n if (stream === this._remoteStreamValue && signature === this._lastTrackSignature) {\n return;\n }\n this._remoteStreamValue = stream;\n this._lastTrackSignature = signature;\n this.requestUpdate();\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement;\n if (video) attachMediaStream(video, stream);\n })\n );\n }\n\n private _computeTrackSignature(stream: MediaStream | null): string {\n if (!stream) return '';\n return stream.getTracks().map((t) => `${t.kind}:${t.id}`).sort().join('|');\n }\n\n private _cleanupDirectSubscriptions(): void {\n this._subscriptions.forEach((sub) => sub.unsubscribe());\n this._subscriptions = [];\n }\n\n // ── Audio output ───────────────────────────────────────────────────\n\n private _applySinkId(deviceId: string): void {\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement & {\n setSinkId?: (sinkId: string) => Promise<void>;\n };\n if (!video?.setSinkId) return;\n video.setSinkId(deviceId).catch((err) => {\n logger.error('[SwCallMedia] Failed to set audio output device:', err);\n });\n }\n\n // ── Resize / dimension management ──────────────────────────────────\n\n private _setupResizeObserver(): void {\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement;\n const paddingWrapper = this.shadowRoot?.querySelector('.padding-wrapper') as HTMLDivElement;\n if (!video || !paddingWrapper) return;\n\n this._videoElement = video;\n this._paddingWrapper = paddingWrapper;\n\n this._resizeObserver = new ResizeObserver(\n debounce(() => this._recalculateDimensions(), 50)\n );\n this._resizeObserver.observe(this);\n\n this._videoResizeHandler = () => this._recalculateDimensions();\n video.addEventListener('resize', this._videoResizeHandler);\n\n this._windowResizeHandler = () => {\n requestAnimationFrame(() => this._recalculateDimensions());\n };\n window.addEventListener('resize', this._windowResizeHandler);\n\n this._recalculateDimensions();\n }\n\n private _recalculateDimensions(): void {\n const video = this._videoElement;\n const paddingWrapper = this._paddingWrapper;\n if (!video || !paddingWrapper) return;\n\n if (!video.videoWidth || !video.videoHeight) {\n paddingWrapper.style.width = '100%';\n paddingWrapper.style.height = '100%';\n paddingWrapper.style.paddingBottom = '0';\n paddingWrapper.style.transform = 'none';\n return;\n }\n\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const containerRect = this.getBoundingClientRect();\n const containerWidth = containerRect.width;\n const containerHeight = containerRect.height;\n if (containerWidth <= 0 || containerHeight <= 0) return;\n\n const scaleX = Math.min(1, (viewportWidth - containerRect.left) / containerWidth);\n const scaleY = Math.min(1, (viewportHeight - containerRect.top) / containerHeight);\n const scale = Math.min(scaleX, scaleY);\n\n const maxWidth = containerWidth * scale;\n const maxHeight = containerHeight * scale;\n if (maxWidth <= 0 || maxHeight <= 0) return;\n\n const videoAspectRatio = video.videoWidth / video.videoHeight;\n const availableAspectRatio = maxWidth / maxHeight;\n\n let finalWidth: number;\n let finalHeight: number;\n\n if (videoAspectRatio > availableAspectRatio) {\n finalWidth = maxWidth;\n finalHeight = finalWidth / videoAspectRatio;\n } else {\n finalHeight = maxHeight;\n finalWidth = finalHeight * videoAspectRatio;\n }\n\n const visibleLeft = Math.max(0, containerRect.left);\n const visibleTop = Math.max(0, containerRect.top);\n const visibleRight = Math.min(viewportWidth, containerRect.right);\n const visibleBottom = Math.min(viewportHeight, containerRect.bottom);\n\n const visibleCenterX = (visibleLeft + visibleRight) / 2;\n const visibleCenterY = (visibleTop + visibleBottom) / 2;\n const containerCenterX = containerRect.left + containerWidth / 2;\n const containerCenterY = containerRect.top + containerHeight / 2;\n\n const offsetX = visibleCenterX - containerCenterX;\n const offsetY = visibleCenterY - containerCenterY;\n\n paddingWrapper.style.width = `${finalWidth}px`;\n paddingWrapper.style.height = `${finalHeight}px`;\n paddingWrapper.style.paddingBottom = '0';\n paddingWrapper.style.transform = `translate(${offsetX}px, ${offsetY}px)`;\n }\n\n private _cleanupResizeObserver(): void {\n if (this._resizeObserver) {\n this._resizeObserver.disconnect();\n this._resizeObserver = undefined;\n }\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement;\n if (video && this._videoResizeHandler) {\n video.removeEventListener('resize', this._videoResizeHandler);\n this._videoResizeHandler = undefined;\n }\n if (this._windowResizeHandler) {\n window.removeEventListener('resize', this._windowResizeHandler);\n this._windowResizeHandler = undefined;\n }\n }\n\n private _cleanupVideoElement(): void {\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement;\n if (video) detachMediaStream(video);\n }\n\n // ── Render ─────────────────────────────────────────────────────────\n\n render() {\n return html`\n <div class=\"mcu-content\" part=\"container\">\n <div class=\"padding-wrapper\">\n <div class=\"mcu-wrapper\">\n <video\n class=\"mcu-video\"\n part=\"video\"\n autoplay\n playsinline\n .srcObject=${this._remoteStreamValue}\n ></video>\n </div>\n <div class=\"mcu-layers\">\n <slot></slot>\n </div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-call-media': SwCallMedia;\n }\n}\n"],"names":["logger","getLogger","SwCallMedia","LitElement","changedProperties","stream","signature","video","_a","attachMediaStream","_b","_c","_e","_d","waitForVideoReady","t","sub","deviceId","err","paddingWrapper","debounce","viewportWidth","viewportHeight","containerRect","containerWidth","containerHeight","scaleX","scaleY","scale","maxWidth","maxHeight","videoAspectRatio","availableAspectRatio","finalWidth","finalHeight","visibleLeft","visibleTop","visibleRight","visibleBottom","visibleCenterX","visibleCenterY","containerCenterX","containerCenterY","offsetX","offsetY","html","css","__decorateClass","property","consume","callStateContext","state","devicesContext","customElement"],"mappings":"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"}
1
+ {"version":3,"file":"sw-call-media.js","sources":["../../src/components/sw-call-media.ts"],"sourcesContent":["/**\n * Call Media Component\n *\n * Renders the remote video stream with aspect-ratio-aware sizing.\n *\n * Input precedence (most specific wins): `.stream` > `.call` > context.\n *\n * @example\n * ```html\n * <!-- Inside a context provider (sw-call-widget): -->\n * <sw-call-media></sw-call-media>\n *\n * <!-- With an explicit Call: -->\n * <sw-call-media .call=${call}></sw-call-media>\n *\n * <!-- With a raw remote stream: -->\n * <sw-call-media .stream=${remoteStream}></sw-call-media>\n * ```\n *\n * @csspart container - Outer container that holds the video and overlay layers.\n * @csspart video - The `<video>` element rendering the remote stream.\n *\n * @slot - Default slot for overlay layers (e.g. `<sw-self-media>`, `<sw-participants>`).\n */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { consume } from '@lit/context';\nimport { Subscription } from 'rxjs';\nimport type { Call } from '../types/index.js';\nimport { callStateContext, type CallState } from '../context/call-state-context.js';\nimport { devicesContext, type DevicesState } from '../context/devices-context.js';\nimport { attachMediaStream, detachMediaStream } from '../utils/video.js';\nimport { getLogger } from '@signalwire/js';\n\nconst logger = getLogger();\n\n@customElement('sw-call-media')\nexport class SwCallMedia extends LitElement {\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n overflow: hidden;\n }\n\n .mcu-content {\n position: relative;\n width: 100%;\n height: 100%;\n margin: 0 auto;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: var(--bg-page, #0e0e18);\n overflow: hidden;\n }\n\n /* Fill the parent box. The video element below uses object-fit: contain\n so the stream letterboxes into whatever rectangle the parent gives us\n — no JS sizing pass required. */\n .padding-wrapper {\n position: relative;\n width: 100%;\n height: 100%;\n }\n\n /* Outer rounding is owned by sw-ui-call-layout's :host border-radius +\n overflow:hidden. Rounding here too carves a notch out of the right\n edge of the video cell that exposes whatever sits behind the widget\n (page / modal backdrop) as a black sliver in light mode. */\n .mcu-wrapper {\n position: absolute;\n inset: 0;\n overflow: hidden;\n }\n\n .mcu-video {\n width: 100%;\n height: 100%;\n display: block;\n object-fit: contain;\n }\n\n .mcu-layers {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n }\n `;\n\n /**\n * Explicit Call — when set, subscribes directly to its observables instead\n * of relying on context. Overridden by `.stream` if both are set.\n */\n @property({ type: Object }) call?: Call;\n\n /**\n * Explicit remote MediaStream — highest precedence. Bypasses both `.call`\n * and context. Useful for raw rendering with no SDK at all.\n */\n @property({ attribute: false }) stream: MediaStream | null = null;\n\n @consume({ context: callStateContext, subscribe: true })\n @state()\n private _callState?: CallState;\n\n @consume({ context: devicesContext, subscribe: true })\n @state()\n private _devicesState?: DevicesState;\n\n private _remoteStreamValue: MediaStream | null = null;\n private _lastTrackSignature = '';\n private _subscriptions: Subscription[] = [];\n\n // ── Lifecycle ──────────────────────────────────────────────────────\n\n connectedCallback() {\n super.connectedCallback();\n if (!this.stream && this.call) this._setupDirectSubscriptions();\n if (this.stream) {\n this._remoteStreamValue = this.stream;\n this._lastTrackSignature = this._computeTrackSignature(this.stream);\n }\n }\n\n protected updated(changedProperties: Map<string, unknown>): void {\n super.updated(changedProperties);\n\n // Direct call prop changed — re-subscribe (only meaningful when `.stream` isn't set)\n if (changedProperties.has('call')) {\n this._cleanupDirectSubscriptions();\n if (!this.stream && this.call) this._setupDirectSubscriptions();\n }\n\n // Explicit `.stream` prop wins — attach it and skip everything else.\n if (changedProperties.has('stream')) {\n this._cleanupDirectSubscriptions();\n const stream = this.stream;\n const signature = this._computeTrackSignature(stream);\n if (stream !== this._remoteStreamValue || signature !== this._lastTrackSignature) {\n this._remoteStreamValue = stream;\n this._lastTrackSignature = signature;\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement;\n if (video) attachMediaStream(video, stream);\n }\n }\n\n // Context-driven: stream or its track set changed.\n // WebRTC delivers tracks one at a time via ontrack, and the SDK re-emits\n // the same MediaStream reference each time — so we must also re-attach when\n // the track set changes, otherwise Chromium may never render a video track\n // added after the initial srcObject assignment.\n if (!this.stream && !this.call && changedProperties.has('_callState')) {\n const stream = this._callState?.remoteStream ?? null;\n const signature = this._computeTrackSignature(stream);\n if (stream !== this._remoteStreamValue || signature !== this._lastTrackSignature) {\n this._remoteStreamValue = stream;\n this._lastTrackSignature = signature;\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement;\n if (video) attachMediaStream(video, stream);\n }\n }\n\n // Audio output device changed\n if (changedProperties.has('_devicesState')) {\n this._applySinkId(this._devicesState?.selectedAudioOutput?.deviceId ?? '');\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this._cleanupDirectSubscriptions();\n this._cleanupVideoElement();\n }\n\n // ── Direct call subscriptions (legacy / standalone) ────────────────\n\n private _setupDirectSubscriptions(): void {\n if (!this.call) return;\n this._subscriptions.push(\n this.call.remoteStream$.subscribe((stream: MediaStream | null) => {\n const signature = this._computeTrackSignature(stream);\n if (stream === this._remoteStreamValue && signature === this._lastTrackSignature) {\n return;\n }\n this._remoteStreamValue = stream;\n this._lastTrackSignature = signature;\n this.requestUpdate();\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement;\n if (video) attachMediaStream(video, stream);\n })\n );\n }\n\n private _computeTrackSignature(stream: MediaStream | null): string {\n if (!stream) return '';\n return stream.getTracks().map((t) => `${t.kind}:${t.id}`).sort().join('|');\n }\n\n private _cleanupDirectSubscriptions(): void {\n this._subscriptions.forEach((sub) => sub.unsubscribe());\n this._subscriptions = [];\n }\n\n // ── Audio output ───────────────────────────────────────────────────\n\n private _applySinkId(deviceId: string): void {\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement & {\n setSinkId?: (sinkId: string) => Promise<void>;\n };\n if (!video?.setSinkId) return;\n video.setSinkId(deviceId).catch((err) => {\n logger.error('[SwCallMedia] Failed to set audio output device:', err);\n });\n }\n\n // ── Cleanup ─────────────────────────────────────────────────────────\n\n private _cleanupVideoElement(): void {\n const video = this.shadowRoot?.querySelector('video.mcu-video') as HTMLVideoElement;\n if (video) detachMediaStream(video);\n }\n\n // ── Render ─────────────────────────────────────────────────────────\n\n render() {\n return html`\n <div class=\"mcu-content\" part=\"container\">\n <div class=\"padding-wrapper\">\n <div class=\"mcu-wrapper\">\n <video\n class=\"mcu-video\"\n part=\"video\"\n autoplay\n playsinline\n muted\n .srcObject=${this._remoteStreamValue}\n ></video>\n </div>\n <div class=\"mcu-layers\">\n <slot></slot>\n </div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-call-media': SwCallMedia;\n }\n}\n"],"names":["logger","getLogger","SwCallMedia","LitElement","changedProperties","stream","signature","video","_a","attachMediaStream","_b","_c","_e","_d","t","sub","deviceId","err","html","css","__decorateClass","property","consume","callStateContext","state","devicesContext","customElement"],"mappings":"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"}