@signalwire/web-components 1.0.0-dev-20260311213302 → 1.0.0-dev-20260318131609
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -9
- package/dist/components/call-status.d.ts +1 -4
- package/dist/components/call-status.d.ts.map +1 -1
- package/dist/components/call-status.js +55 -52
- package/dist/components/call-status.js.map +1 -1
- package/dist/components/device-selector.d.ts +1 -27
- package/dist/components/device-selector.d.ts.map +1 -1
- package/dist/components/device-selector.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -23
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +2 -25
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -214,7 +214,7 @@ Displays call state, status text, and duration timer.
|
|
|
214
214
|
|----------|------|---------|-------------|
|
|
215
215
|
| `call` | `CallStatusCall` | - | Call with status$ observable |
|
|
216
216
|
|
|
217
|
-
**Status Values:** `
|
|
217
|
+
**Status Values:** `new`, `trying`, `ringing`, `connecting`, `connected`, `disconnecting`, `disconnected`, `failed`, `destroyed`
|
|
218
218
|
|
|
219
219
|
**CSS Custom Properties:**
|
|
220
220
|
|
|
@@ -314,7 +314,7 @@ Dropdown for selecting audio/video input and output devices.
|
|
|
314
314
|
| Event | Detail | Description |
|
|
315
315
|
|-------|--------|-------------|
|
|
316
316
|
| `sw-tab-change` | `{ tab: 'microphone' \| 'camera' \| 'speaker' }` | Tab changed |
|
|
317
|
-
| `sw-device-change` | `{ device:
|
|
317
|
+
| `sw-device-change` | `{ device: MediaDeviceInfo }` | Device selected |
|
|
318
318
|
| `sw-test-speaker` | - | Speaker test triggered |
|
|
319
319
|
|
|
320
320
|
**CSS Custom Properties:**
|
|
@@ -668,7 +668,7 @@ sw-call-media::part(video) {
|
|
|
668
668
|
## TypeScript Interfaces
|
|
669
669
|
|
|
670
670
|
```typescript
|
|
671
|
-
import type { Call,
|
|
671
|
+
import type { Call, CallSelf, DeviceController, LayoutLayer, Participant } from '@signalwire/web-components';
|
|
672
672
|
|
|
673
673
|
// Call object interface
|
|
674
674
|
interface Call {
|
|
@@ -694,12 +694,9 @@ interface LayoutLayer {
|
|
|
694
694
|
height: number;
|
|
695
695
|
}
|
|
696
696
|
|
|
697
|
-
//
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
label: string;
|
|
701
|
-
kind: 'audioinput' | 'audiooutput' | 'videoinput';
|
|
702
|
-
}
|
|
697
|
+
// DeviceController is imported from @signalwire/js via @signalwire/web-components
|
|
698
|
+
// It provides observables for device lists and selection methods.
|
|
699
|
+
// See @signalwire/js documentation for the full DeviceController interface.
|
|
703
700
|
|
|
704
701
|
// Directory address
|
|
705
702
|
interface Address {
|
|
@@ -11,10 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { LitElement } from 'lit';
|
|
13
13
|
import type { Observable } from 'rxjs';
|
|
14
|
-
|
|
15
|
-
* Call status types
|
|
16
|
-
*/
|
|
17
|
-
export type CallStatus = 'new' | 'trying' | 'idle' | 'connecting' | 'ringing' | 'connected' | 'disconnecting' | 'disconnected' | 'failed' | 'destroyed';
|
|
14
|
+
import type { CallStatus } from '@signalwire/js';
|
|
18
15
|
/**
|
|
19
16
|
* Call interface for status component
|
|
20
17
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"call-status.d.ts","sourceRoot":"","sources":["../../src/components/call-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAI5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"call-status.d.ts","sourceRoot":"","sources":["../../src/components/call-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAI5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;CACjC;AAED,qBACa,mBAAoB,SAAQ,UAAU;IACjD,MAAM,CAAC,MAAM,0BAsIX;IAEF;;OAEG;IAEH,IAAI,EAAE,cAAc,GAAG,IAAI,CAAQ;IAEnC;;OAEG;IAGH,OAAO,CAAC,WAAW,CAAC,CAAiB;IAErC;;OAEG;IAEH,OAAO,CAAC,MAAM,CAAqB;IAEnC;;OAEG;IAEH,OAAO,CAAC,aAAa,CAAuB;IAE5C;;OAEG;IAEH,OAAO,CAAC,QAAQ,CAAkB;IAElC;;OAEG;IACH,OAAO,CAAC,aAAa,CAAsB;IAE3C;;OAEG;IACH,OAAO,CAAC,gBAAgB,CAAuB;IAE/C,iBAAiB;IAKjB,oBAAoB;IAKpB,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAO/C,OAAO,KAAK,UAAU,GAErB;IAED,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,aAAa;IA2BrB,MAAM;CAqBP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,gBAAgB,EAAE,mBAAmB,CAAC;KACvC;CACF"}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { LitElement as d, html as u, css as p } from "lit";
|
|
2
|
-
import { property as
|
|
2
|
+
import { property as h, state as i, customElement as f } from "lit/decorators.js";
|
|
3
3
|
import { consume as m } from "@lit/context";
|
|
4
4
|
import { callContext as w } from "../context/call-context.js";
|
|
5
|
-
var g = Object.defineProperty,
|
|
6
|
-
for (var
|
|
7
|
-
(l = t[c]) && (
|
|
8
|
-
return a &&
|
|
5
|
+
var g = Object.defineProperty, x = Object.getOwnPropertyDescriptor, n = (t, s, e, a) => {
|
|
6
|
+
for (var o = a > 1 ? void 0 : a ? x(s, e) : s, c = t.length - 1, l; c >= 0; c--)
|
|
7
|
+
(l = t[c]) && (o = (a ? l(s, e, o) : l(o)) || o);
|
|
8
|
+
return a && o && g(s, e, o), o;
|
|
9
9
|
};
|
|
10
|
-
let
|
|
10
|
+
let r = class extends d {
|
|
11
11
|
constructor() {
|
|
12
|
-
super(...arguments), this.call = null, this.status = "
|
|
12
|
+
super(...arguments), this.call = null, this.status = "new", this.callStartTime = null, this.duration = "0:00", this.subscriptions = [], this.durationInterval = null;
|
|
13
13
|
}
|
|
14
14
|
connectedCallback() {
|
|
15
15
|
super.connectedCallback(), this.subscribeToCall();
|
|
@@ -52,8 +52,10 @@ let n = class extends d {
|
|
|
52
52
|
}
|
|
53
53
|
getStatusText() {
|
|
54
54
|
switch (this.status) {
|
|
55
|
-
case "
|
|
56
|
-
return "
|
|
55
|
+
case "new":
|
|
56
|
+
return "Ready";
|
|
57
|
+
case "trying":
|
|
58
|
+
return "Trying...";
|
|
57
59
|
case "connecting":
|
|
58
60
|
return "Connecting...";
|
|
59
61
|
case "ringing":
|
|
@@ -64,8 +66,14 @@ let n = class extends d {
|
|
|
64
66
|
return "Disconnecting...";
|
|
65
67
|
case "disconnected":
|
|
66
68
|
return "Disconnected";
|
|
67
|
-
|
|
68
|
-
return "
|
|
69
|
+
case "failed":
|
|
70
|
+
return "Failed";
|
|
71
|
+
case "destroyed":
|
|
72
|
+
return "Ended";
|
|
73
|
+
default: {
|
|
74
|
+
const t = this.status;
|
|
75
|
+
return String(t);
|
|
76
|
+
}
|
|
69
77
|
}
|
|
70
78
|
}
|
|
71
79
|
render() {
|
|
@@ -86,7 +94,7 @@ let n = class extends d {
|
|
|
86
94
|
`;
|
|
87
95
|
}
|
|
88
96
|
};
|
|
89
|
-
|
|
97
|
+
r.styles = p`
|
|
90
98
|
:host {
|
|
91
99
|
/* CSS Custom Properties for theming */
|
|
92
100
|
--sw-color-primary: #044cf6;
|
|
@@ -140,7 +148,7 @@ n.styles = p`
|
|
|
140
148
|
flex-shrink: 0;
|
|
141
149
|
}
|
|
142
150
|
|
|
143
|
-
.status-indicator.
|
|
151
|
+
.status-indicator.new {
|
|
144
152
|
background-color: var(--sw-color-text-muted);
|
|
145
153
|
}
|
|
146
154
|
|
|
@@ -159,10 +167,20 @@ n.styles = p`
|
|
|
159
167
|
animation: pulse 1s ease-in-out infinite;
|
|
160
168
|
}
|
|
161
169
|
|
|
162
|
-
.status-indicator.
|
|
170
|
+
.status-indicator.trying {
|
|
171
|
+
background-color: var(--sw-color-warning);
|
|
172
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.status-indicator.disconnected,
|
|
176
|
+
.status-indicator.failed {
|
|
163
177
|
background-color: var(--sw-color-danger);
|
|
164
178
|
}
|
|
165
179
|
|
|
180
|
+
.status-indicator.destroyed {
|
|
181
|
+
background-color: var(--sw-color-text-muted);
|
|
182
|
+
}
|
|
183
|
+
|
|
166
184
|
@keyframes pulse {
|
|
167
185
|
0%,
|
|
168
186
|
100% {
|
|
@@ -180,6 +198,7 @@ n.styles = p`
|
|
|
180
198
|
white-space: nowrap;
|
|
181
199
|
}
|
|
182
200
|
|
|
201
|
+
.status-text.trying,
|
|
183
202
|
.status-text.connecting,
|
|
184
203
|
.status-text.ringing {
|
|
185
204
|
color: var(--sw-color-warning);
|
|
@@ -193,59 +212,43 @@ n.styles = p`
|
|
|
193
212
|
color: var(--sw-color-success);
|
|
194
213
|
}
|
|
195
214
|
|
|
196
|
-
.status-text.disconnected
|
|
215
|
+
.status-text.disconnected,
|
|
216
|
+
.status-text.failed {
|
|
197
217
|
color: var(--sw-color-danger);
|
|
198
218
|
}
|
|
199
219
|
|
|
220
|
+
.status-text.new,
|
|
221
|
+
.status-text.destroyed {
|
|
222
|
+
color: var(--sw-color-text-muted);
|
|
223
|
+
}
|
|
224
|
+
|
|
200
225
|
.duration {
|
|
201
226
|
font-variant-numeric: tabular-nums;
|
|
202
227
|
color: var(--sw-color-text-muted);
|
|
203
228
|
font-size: var(--sw-font-size-sm);
|
|
204
229
|
}
|
|
205
230
|
|
|
206
|
-
/* Animated dots for transient states */
|
|
207
|
-
.loading-dots::after {
|
|
208
|
-
content: '';
|
|
209
|
-
animation: dots 1.5s steps(4, end) infinite;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
@keyframes dots {
|
|
213
|
-
0%,
|
|
214
|
-
20% {
|
|
215
|
-
content: '';
|
|
216
|
-
}
|
|
217
|
-
40% {
|
|
218
|
-
content: '.';
|
|
219
|
-
}
|
|
220
|
-
60% {
|
|
221
|
-
content: '..';
|
|
222
|
-
}
|
|
223
|
-
80%,
|
|
224
|
-
100% {
|
|
225
|
-
content: '...';
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
231
|
`;
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
],
|
|
232
|
-
|
|
232
|
+
n([
|
|
233
|
+
h({ attribute: !1 })
|
|
234
|
+
], r.prototype, "call", 2);
|
|
235
|
+
n([
|
|
233
236
|
m({ context: w, subscribe: !0 }),
|
|
234
237
|
i()
|
|
235
|
-
],
|
|
236
|
-
|
|
238
|
+
], r.prototype, "contextCall", 2);
|
|
239
|
+
n([
|
|
237
240
|
i()
|
|
238
|
-
],
|
|
239
|
-
|
|
241
|
+
], r.prototype, "status", 2);
|
|
242
|
+
n([
|
|
240
243
|
i()
|
|
241
|
-
],
|
|
242
|
-
|
|
244
|
+
], r.prototype, "callStartTime", 2);
|
|
245
|
+
n([
|
|
243
246
|
i()
|
|
244
|
-
],
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
],
|
|
247
|
+
], r.prototype, "duration", 2);
|
|
248
|
+
r = n([
|
|
249
|
+
f("sw-call-status")
|
|
250
|
+
], r);
|
|
248
251
|
export {
|
|
249
|
-
|
|
252
|
+
r as CallStatusComponent
|
|
250
253
|
};
|
|
251
254
|
//# sourceMappingURL=call-status.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"call-status.js","sources":["../../src/components/call-status.ts"],"sourcesContent":["/**\n * Call Status Component\n *\n * Displays current call state with status text and duration timer.\n * Shows animated indicators for transient states (connecting, ringing, disconnecting).\n *\n * @example\n * ```html\n * <sw-call-status .call=${call}></sw-call-status>\n * ```\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 { Observable } from 'rxjs';\nimport { callContext } from '../context/index.js';\n\n/**\n * Call status types\n */\nexport type CallStatus =\n | 'new'\n | 'trying'\n | 'idle'\n | 'connecting'\n | 'ringing'\n | 'connected'\n | 'disconnecting'\n | 'disconnected'\n | 'failed'\n | 'destroyed';\n\n/**\n * Call interface for status component\n */\nexport interface CallStatusCall {\n status$: Observable<CallStatus>;\n}\n\n@customElement('sw-call-status')\nexport class CallStatusComponent extends LitElement {\n static styles = css`\n :host {\n /* CSS Custom Properties for theming */\n --sw-color-primary: #044cf6;\n --sw-color-success: #10b981;\n --sw-color-warning: #f59e0b;\n --sw-color-danger: #ef4444;\n --sw-color-text: #1f2937;\n --sw-color-text-muted: #6b7280;\n --sw-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n --sw-font-size-sm: 14px;\n --sw-font-size-base: 16px;\n --sw-font-size-lg: 18px;\n --sw-space-1: 4px;\n --sw-space-2: 8px;\n --sw-space-3: 12px;\n --sw-border-radius: 8px;\n\n display: inline-flex;\n align-items: center;\n gap: var(--sw-space-2);\n font-family: var(--sw-font-family);\n font-size: var(--sw-font-size-base);\n }\n\n /* Dark mode support */\n :host([data-theme='dark']) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n }\n\n @media (prefers-color-scheme: dark) {\n :host(:not([data-theme='light'])) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n }\n }\n\n .container {\n display: inline-flex;\n align-items: center;\n gap: var(--sw-space-2);\n padding: var(--sw-space-2) var(--sw-space-3);\n border-radius: var(--sw-border-radius);\n color: var(--sw-color-text);\n }\n\n .status-indicator {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n .status-indicator.idle {\n background-color: var(--sw-color-text-muted);\n }\n\n .status-indicator.connecting,\n .status-indicator.ringing {\n background-color: var(--sw-color-warning);\n animation: pulse 1.5s ease-in-out infinite;\n }\n\n .status-indicator.connected {\n background-color: var(--sw-color-success);\n }\n\n .status-indicator.disconnecting {\n background-color: var(--sw-color-danger);\n animation: pulse 1s ease-in-out infinite;\n }\n\n .status-indicator.disconnected {\n background-color: var(--sw-color-danger);\n }\n\n @keyframes pulse {\n 0%,\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n 50% {\n opacity: 0.5;\n transform: scale(1.1);\n }\n }\n\n .status-text {\n font-weight: 500;\n white-space: nowrap;\n }\n\n .status-text.connecting,\n .status-text.ringing {\n color: var(--sw-color-warning);\n }\n\n .status-text.disconnecting {\n color: var(--sw-color-danger);\n }\n\n .status-text.connected {\n color: var(--sw-color-success);\n }\n\n .status-text.disconnected {\n color: var(--sw-color-danger);\n }\n\n .duration {\n font-variant-numeric: tabular-nums;\n color: var(--sw-color-text-muted);\n font-size: var(--sw-font-size-sm);\n }\n\n /* Animated dots for transient states */\n .loading-dots::after {\n content: '';\n animation: dots 1.5s steps(4, end) infinite;\n }\n\n @keyframes dots {\n 0%,\n 20% {\n content: '';\n }\n 40% {\n content: '.';\n }\n 60% {\n content: '..';\n }\n 80%,\n 100% {\n content: '...';\n }\n }\n `;\n\n /**\n * Call object with status$ observable\n */\n @property({ attribute: false })\n call: CallStatusCall | null = null;\n\n /**\n * Call from context (if nested in sw-call-media)\n */\n @consume({ context: callContext, subscribe: true })\n @state()\n private contextCall?: CallStatusCall;\n\n /**\n * Current call status\n */\n @state()\n private status: CallStatus = 'idle';\n\n /**\n * Call start time for duration calculation\n */\n @state()\n private callStartTime: number | null = null;\n\n /**\n * Formatted duration string\n */\n @state()\n private duration: string = '0:00';\n\n /**\n * RxJS subscriptions for cleanup\n */\n private subscriptions: Subscription[] = [];\n\n /**\n * Duration timer interval\n */\n private durationInterval: number | null = null;\n\n connectedCallback() {\n super.connectedCallback();\n this.subscribeToCall();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.cleanup();\n }\n\n updated(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('call') || changedProperties.has('contextCall')) {\n this.cleanup();\n this.subscribeToCall();\n }\n }\n\n private get activeCall(): CallStatusCall | null {\n return this.call || this.contextCall || null;\n }\n\n private subscribeToCall() {\n const call = this.activeCall;\n if (!call?.status$) return;\n\n const statusSub = call.status$.subscribe((status) => {\n const prevStatus = this.status;\n this.status = status;\n\n // Start duration timer when connected\n if (status === 'connected' && prevStatus !== 'connected') {\n this.startDurationTimer();\n } else if (status !== 'connected') {\n this.stopDurationTimer();\n }\n });\n\n this.subscriptions.push(statusSub);\n }\n\n private startDurationTimer() {\n this.callStartTime = Date.now();\n this.duration = '0:00';\n\n this.durationInterval = window.setInterval(() => {\n if (this.callStartTime) {\n const elapsed = Math.floor((Date.now() - this.callStartTime) / 1000);\n this.duration = this.formatDuration(elapsed);\n }\n }, 1000);\n }\n\n private stopDurationTimer() {\n if (this.durationInterval) {\n clearInterval(this.durationInterval);\n this.durationInterval = null;\n }\n this.callStartTime = null;\n this.duration = '0:00';\n }\n\n private formatDuration(seconds: number): string {\n const hours = Math.floor(seconds / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n const secs = seconds % 60;\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;\n }\n return `${minutes}:${secs.toString().padStart(2, '0')}`;\n }\n\n private cleanup() {\n this.subscriptions.forEach((sub) => sub.unsubscribe());\n this.subscriptions = [];\n this.stopDurationTimer();\n }\n\n private getStatusText(): string {\n switch (this.status) {\n case 'idle':\n return 'Idle';\n case 'connecting':\n return 'Connecting...';\n case 'ringing':\n return 'Ringing...';\n case 'connected':\n return 'Connected';\n case 'disconnecting':\n return 'Disconnecting...';\n case 'disconnected':\n return 'Disconnected';\n default:\n return 'Unknown';\n }\n }\n\n render() {\n const statusText = this.getStatusText();\n const showDuration = this.status === 'connected';\n\n return html`\n <div class=\"container\" part=\"container\">\n <span class=\"status-indicator ${this.status}\" aria-hidden=\"true\"></span>\n <span\n class=\"status-text ${this.status}\"\n part=\"status-text\"\n role=\"status\"\n aria-live=\"polite\"\n >\n ${statusText}\n </span>\n ${showDuration\n ? html`<span class=\"duration\" part=\"duration\">${this.duration}</span>`\n : null}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-call-status': CallStatusComponent;\n }\n}\n"],"names":["CallStatusComponent","LitElement","changedProperties","call","statusSub","status","prevStatus","elapsed","seconds","hours","minutes","secs","sub","statusText","showDuration","html","css","__decorateClass","property","consume","callContext","state","customElement"],"mappings":";;;;;;;;;AA0CO,IAAMA,IAAN,cAAkCC,EAAW;AAAA,EAA7C,cAAA;AAAA,UAAA,GAAA,SAAA,GAkJL,KAAA,OAA8B,MAa9B,KAAQ,SAAqB,QAM7B,KAAQ,gBAA+B,MAMvC,KAAQ,WAAmB,QAK3B,KAAQ,gBAAgC,CAAA,GAKxC,KAAQ,mBAAkC;AAAA,EAAA;AAAA,EAE1C,oBAAoB;AAClB,UAAM,kBAAA,GACN,KAAK,gBAAA;AAAA,EACP;AAAA,EAEA,uBAAuB;AACrB,UAAM,qBAAA,GACN,KAAK,QAAA;AAAA,EACP;AAAA,EAEA,QAAQC,GAAyC;AAC/C,KAAIA,EAAkB,IAAI,MAAM,KAAKA,EAAkB,IAAI,aAAa,OACtE,KAAK,QAAA,GACL,KAAK,gBAAA;AAAA,EAET;AAAA,EAEA,IAAY,aAAoC;AAC9C,WAAO,KAAK,QAAQ,KAAK,eAAe;AAAA,EAC1C;AAAA,EAEQ,kBAAkB;AACxB,UAAMC,IAAO,KAAK;AAClB,QAAI,EAACA,KAAA,QAAAA,EAAM,SAAS;AAEpB,UAAMC,IAAYD,EAAK,QAAQ,UAAU,CAACE,MAAW;AACnD,YAAMC,IAAa,KAAK;AACxB,WAAK,SAASD,GAGVA,MAAW,eAAeC,MAAe,cAC3C,KAAK,mBAAA,IACID,MAAW,eACpB,KAAK,kBAAA;AAAA,IAET,CAAC;AAED,SAAK,cAAc,KAAKD,CAAS;AAAA,EACnC;AAAA,EAEQ,qBAAqB;AAC3B,SAAK,gBAAgB,KAAK,IAAA,GAC1B,KAAK,WAAW,QAEhB,KAAK,mBAAmB,OAAO,YAAY,MAAM;AAC/C,UAAI,KAAK,eAAe;AACtB,cAAMG,IAAU,KAAK,OAAO,KAAK,QAAQ,KAAK,iBAAiB,GAAI;AACnE,aAAK,WAAW,KAAK,eAAeA,CAAO;AAAA,MAC7C;AAAA,IACF,GAAG,GAAI;AAAA,EACT;AAAA,EAEQ,oBAAoB;AAC1B,IAAI,KAAK,qBACP,cAAc,KAAK,gBAAgB,GACnC,KAAK,mBAAmB,OAE1B,KAAK,gBAAgB,MACrB,KAAK,WAAW;AAAA,EAClB;AAAA,EAEQ,eAAeC,GAAyB;AAC9C,UAAMC,IAAQ,KAAK,MAAMD,IAAU,IAAI,GACjCE,IAAU,KAAK,MAAOF,IAAU,OAAQ,EAAE,GAC1CG,IAAOH,IAAU;AAEvB,WAAIC,IAAQ,IACH,GAAGA,CAAK,IAAIC,EAAQ,SAAA,EAAW,SAAS,GAAG,GAAG,CAAC,IAAIC,EAAK,SAAA,EAAW,SAAS,GAAG,GAAG,CAAC,KAErF,GAAGD,CAAO,IAAIC,EAAK,WAAW,SAAS,GAAG,GAAG,CAAC;AAAA,EACvD;AAAA,EAEQ,UAAU;AAChB,SAAK,cAAc,QAAQ,CAACC,MAAQA,EAAI,aAAa,GACrD,KAAK,gBAAgB,CAAA,GACrB,KAAK,kBAAA;AAAA,EACP;AAAA,EAEQ,gBAAwB;AAC9B,YAAQ,KAAK,QAAA;AAAA,MACX,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IAAA;AAAA,EAEb;AAAA,EAEA,SAAS;AACP,UAAMC,IAAa,KAAK,cAAA,GAClBC,IAAe,KAAK,WAAW;AAErC,WAAOC;AAAA;AAAA,wCAE6B,KAAK,MAAM;AAAA;AAAA,+BAEpB,KAAK,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,YAK9BF,CAAU;AAAA;AAAA,UAEZC,IACEC,2CAA8C,KAAK,QAAQ,YAC3D,IAAI;AAAA;AAAA;AAAA,EAGd;AACF;AA7Saf,EACJ,SAASgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiJhBC,EAAA;AAAA,EADCC,EAAS,EAAE,WAAW,GAAA,CAAO;AAAA,GAjJnBlB,EAkJX,WAAA,QAAA,CAAA;AAOQiB,EAAA;AAAA,EAFPE,EAAQ,EAAE,SAASC,GAAa,WAAW,IAAM;AAAA,EACjDC,EAAA;AAAM,GAxJIrB,EAyJH,WAAA,eAAA,CAAA;AAMAiB,EAAA;AAAA,EADPI,EAAA;AAAM,GA9JIrB,EA+JH,WAAA,UAAA,CAAA;AAMAiB,EAAA;AAAA,EADPI,EAAA;AAAM,GApKIrB,EAqKH,WAAA,iBAAA,CAAA;AAMAiB,EAAA;AAAA,EADPI,EAAA;AAAM,GA1KIrB,EA2KH,WAAA,YAAA,CAAA;AA3KGA,IAANiB,EAAA;AAAA,EADNK,EAAc,gBAAgB;AAAA,GAClBtB,CAAA;"}
|
|
1
|
+
{"version":3,"file":"call-status.js","sources":["../../src/components/call-status.ts"],"sourcesContent":["/**\n * Call Status Component\n *\n * Displays current call state with status text and duration timer.\n * Shows animated indicators for transient states (connecting, ringing, disconnecting).\n *\n * @example\n * ```html\n * <sw-call-status .call=${call}></sw-call-status>\n * ```\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 { Observable } from 'rxjs';\nimport type { CallStatus } from '@signalwire/js';\nimport { callContext } from '../context/index.js';\n\n/**\n * Call interface for status component\n */\nexport interface CallStatusCall {\n status$: Observable<CallStatus>;\n}\n\n@customElement('sw-call-status')\nexport class CallStatusComponent extends LitElement {\n static styles = css`\n :host {\n /* CSS Custom Properties for theming */\n --sw-color-primary: #044cf6;\n --sw-color-success: #10b981;\n --sw-color-warning: #f59e0b;\n --sw-color-danger: #ef4444;\n --sw-color-text: #1f2937;\n --sw-color-text-muted: #6b7280;\n --sw-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n --sw-font-size-sm: 14px;\n --sw-font-size-base: 16px;\n --sw-font-size-lg: 18px;\n --sw-space-1: 4px;\n --sw-space-2: 8px;\n --sw-space-3: 12px;\n --sw-border-radius: 8px;\n\n display: inline-flex;\n align-items: center;\n gap: var(--sw-space-2);\n font-family: var(--sw-font-family);\n font-size: var(--sw-font-size-base);\n }\n\n /* Dark mode support */\n :host([data-theme='dark']) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n }\n\n @media (prefers-color-scheme: dark) {\n :host(:not([data-theme='light'])) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n }\n }\n\n .container {\n display: inline-flex;\n align-items: center;\n gap: var(--sw-space-2);\n padding: var(--sw-space-2) var(--sw-space-3);\n border-radius: var(--sw-border-radius);\n color: var(--sw-color-text);\n }\n\n .status-indicator {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n .status-indicator.new {\n background-color: var(--sw-color-text-muted);\n }\n\n .status-indicator.connecting,\n .status-indicator.ringing {\n background-color: var(--sw-color-warning);\n animation: pulse 1.5s ease-in-out infinite;\n }\n\n .status-indicator.connected {\n background-color: var(--sw-color-success);\n }\n\n .status-indicator.disconnecting {\n background-color: var(--sw-color-danger);\n animation: pulse 1s ease-in-out infinite;\n }\n\n .status-indicator.trying {\n background-color: var(--sw-color-warning);\n animation: pulse 1.5s ease-in-out infinite;\n }\n\n .status-indicator.disconnected,\n .status-indicator.failed {\n background-color: var(--sw-color-danger);\n }\n\n .status-indicator.destroyed {\n background-color: var(--sw-color-text-muted);\n }\n\n @keyframes pulse {\n 0%,\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n 50% {\n opacity: 0.5;\n transform: scale(1.1);\n }\n }\n\n .status-text {\n font-weight: 500;\n white-space: nowrap;\n }\n\n .status-text.trying,\n .status-text.connecting,\n .status-text.ringing {\n color: var(--sw-color-warning);\n }\n\n .status-text.disconnecting {\n color: var(--sw-color-danger);\n }\n\n .status-text.connected {\n color: var(--sw-color-success);\n }\n\n .status-text.disconnected,\n .status-text.failed {\n color: var(--sw-color-danger);\n }\n\n .status-text.new,\n .status-text.destroyed {\n color: var(--sw-color-text-muted);\n }\n\n .duration {\n font-variant-numeric: tabular-nums;\n color: var(--sw-color-text-muted);\n font-size: var(--sw-font-size-sm);\n }\n\n `;\n\n /**\n * Call object with status$ observable\n */\n @property({ attribute: false })\n call: CallStatusCall | null = null;\n\n /**\n * Call from context (if nested in sw-call-media)\n */\n @consume({ context: callContext, subscribe: true })\n @state()\n private contextCall?: CallStatusCall;\n\n /**\n * Current call status\n */\n @state()\n private status: CallStatus = 'new';\n\n /**\n * Call start time for duration calculation\n */\n @state()\n private callStartTime: number | null = null;\n\n /**\n * Formatted duration string\n */\n @state()\n private duration: string = '0:00';\n\n /**\n * RxJS subscriptions for cleanup\n */\n private subscriptions: Subscription[] = [];\n\n /**\n * Duration timer interval\n */\n private durationInterval: number | null = null;\n\n connectedCallback() {\n super.connectedCallback();\n this.subscribeToCall();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.cleanup();\n }\n\n updated(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('call') || changedProperties.has('contextCall')) {\n this.cleanup();\n this.subscribeToCall();\n }\n }\n\n private get activeCall(): CallStatusCall | null {\n return this.call || this.contextCall || null;\n }\n\n private subscribeToCall() {\n const call = this.activeCall;\n if (!call?.status$) return;\n\n const statusSub = call.status$.subscribe((status) => {\n const prevStatus = this.status;\n this.status = status;\n\n // Start duration timer when connected\n if (status === 'connected' && prevStatus !== 'connected') {\n this.startDurationTimer();\n } else if (status !== 'connected') {\n this.stopDurationTimer();\n }\n });\n\n this.subscriptions.push(statusSub);\n }\n\n private startDurationTimer() {\n this.callStartTime = Date.now();\n this.duration = '0:00';\n\n this.durationInterval = window.setInterval(() => {\n if (this.callStartTime) {\n const elapsed = Math.floor((Date.now() - this.callStartTime) / 1000);\n this.duration = this.formatDuration(elapsed);\n }\n }, 1000);\n }\n\n private stopDurationTimer() {\n if (this.durationInterval) {\n clearInterval(this.durationInterval);\n this.durationInterval = null;\n }\n this.callStartTime = null;\n this.duration = '0:00';\n }\n\n private formatDuration(seconds: number): string {\n const hours = Math.floor(seconds / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n const secs = seconds % 60;\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;\n }\n return `${minutes}:${secs.toString().padStart(2, '0')}`;\n }\n\n private cleanup() {\n this.subscriptions.forEach((sub) => sub.unsubscribe());\n this.subscriptions = [];\n this.stopDurationTimer();\n }\n\n private getStatusText(): string {\n switch (this.status) {\n case 'new':\n return 'Ready';\n case 'trying':\n return 'Trying...';\n case 'connecting':\n return 'Connecting...';\n case 'ringing':\n return 'Ringing...';\n case 'connected':\n return 'Connected';\n case 'disconnecting':\n return 'Disconnecting...';\n case 'disconnected':\n return 'Disconnected';\n case 'failed':\n return 'Failed';\n case 'destroyed':\n return 'Ended';\n default: {\n const _exhaustive: never = this.status;\n return String(_exhaustive);\n }\n }\n }\n\n render() {\n const statusText = this.getStatusText();\n const showDuration = this.status === 'connected';\n\n return html`\n <div class=\"container\" part=\"container\">\n <span class=\"status-indicator ${this.status}\" aria-hidden=\"true\"></span>\n <span\n class=\"status-text ${this.status}\"\n part=\"status-text\"\n role=\"status\"\n aria-live=\"polite\"\n >\n ${statusText}\n </span>\n ${showDuration\n ? html`<span class=\"duration\" part=\"duration\">${this.duration}</span>`\n : null}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-call-status': CallStatusComponent;\n }\n}\n"],"names":["CallStatusComponent","LitElement","changedProperties","call","statusSub","status","prevStatus","elapsed","seconds","hours","minutes","secs","sub","_exhaustive","statusText","showDuration","html","css","__decorateClass","property","consume","callContext","state","customElement"],"mappings":"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"}
|
|
@@ -11,34 +11,8 @@
|
|
|
11
11
|
* ```
|
|
12
12
|
*/
|
|
13
13
|
import { LitElement } from 'lit';
|
|
14
|
-
import type {
|
|
14
|
+
import type { DeviceController } from '@signalwire/js';
|
|
15
15
|
import './audio-level.js';
|
|
16
|
-
/**
|
|
17
|
-
* DeviceController interface for observing device lists
|
|
18
|
-
*/
|
|
19
|
-
export interface DeviceController {
|
|
20
|
-
readonly audioInputDevices$: Observable<MediaDeviceInfo[]>;
|
|
21
|
-
readonly audioOutputDevices$: Observable<MediaDeviceInfo[]>;
|
|
22
|
-
readonly videoInputDevices$: Observable<MediaDeviceInfo[]>;
|
|
23
|
-
readonly selectedAudioInputDevice$: Observable<MediaDeviceInfo | null>;
|
|
24
|
-
readonly selectedAudioOutputDevice$: Observable<MediaDeviceInfo | null>;
|
|
25
|
-
readonly selectedVideoInputDevice$: Observable<MediaDeviceInfo | null>;
|
|
26
|
-
readonly selectedAudioInputDevice: MediaDeviceInfo | null;
|
|
27
|
-
readonly selectedAudioOutputDevice: MediaDeviceInfo | null;
|
|
28
|
-
readonly selectedVideoInputDevice: MediaDeviceInfo | null;
|
|
29
|
-
readonly audioInputDevices: MediaDeviceInfo[];
|
|
30
|
-
readonly audioOutputDevices: MediaDeviceInfo[];
|
|
31
|
-
readonly videoInputDevices: MediaDeviceInfo[];
|
|
32
|
-
readonly selectedAudioInputDeviceConstraints: MediaTrackConstraints;
|
|
33
|
-
readonly selectedVideoInputDeviceConstraints: MediaTrackConstraints;
|
|
34
|
-
MediaDeviceInfoToConstraints(MediaDeviceInfo: MediaDeviceInfo | null): MediaTrackConstraints;
|
|
35
|
-
selectAudioInputDevice(device: MediaDeviceInfo | null): void;
|
|
36
|
-
selectVideoInputDevice(device: MediaDeviceInfo | null): void;
|
|
37
|
-
selectAudioOutputDevice(device: MediaDeviceInfo | null): void;
|
|
38
|
-
enableDeviceMonitoring(): void;
|
|
39
|
-
disableDeviceMonitoring(): void;
|
|
40
|
-
getLocalStream?: () => Promise<MediaStream>;
|
|
41
|
-
}
|
|
42
16
|
export declare class DeviceSelector extends LitElement {
|
|
43
17
|
static styles: import("lit").CSSResult;
|
|
44
18
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device-selector.d.ts","sourceRoot":"","sources":["../../src/components/device-selector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAGrD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"device-selector.d.ts","sourceRoot":"","sources":["../../src/components/device-selector.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device-selector.js","sources":["../../src/components/device-selector.ts"],"sourcesContent":["/**\n * Device Selector Component\n *\n * Dropdown-based selector for choosing audio/video devices. Displays device lists from\n * DeviceController observables with labeled dropdowns for microphone, camera, and speaker.\n * Includes inline preview for video (camera) and audio level (microphone) using browser API.\n *\n * @example\n * ```html\n * <sw-device-selector .deviceController=${deviceController} show-preview></sw-device-selector>\n * ```\n */\n\nimport { LitElement, html, css, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { Subscription } from 'rxjs';\nimport type { Observable } from 'rxjs';\nimport './audio-level.js';\n\n/**\n * DeviceController interface for observing device lists\n */\nexport interface DeviceController {\n // Observable getters for device lists\n readonly audioInputDevices$: Observable<MediaDeviceInfo[]>;\n readonly audioOutputDevices$: Observable<MediaDeviceInfo[]>;\n readonly videoInputDevices$: Observable<MediaDeviceInfo[]>;\n\n // Observable getters for selected devices\n readonly selectedAudioInputDevice$: Observable<MediaDeviceInfo | null>;\n readonly selectedAudioOutputDevice$: Observable<MediaDeviceInfo | null>;\n readonly selectedVideoInputDevice$: Observable<MediaDeviceInfo | null>;\n\n // Current value getters\n readonly selectedAudioInputDevice: MediaDeviceInfo | null;\n readonly selectedAudioOutputDevice: MediaDeviceInfo | null;\n readonly selectedVideoInputDevice: MediaDeviceInfo | null;\n\n readonly audioInputDevices: MediaDeviceInfo[];\n readonly audioOutputDevices: MediaDeviceInfo[];\n readonly videoInputDevices: MediaDeviceInfo[];\n\n // Device constraints\n readonly selectedAudioInputDeviceConstraints: MediaTrackConstraints;\n readonly selectedVideoInputDeviceConstraints: MediaTrackConstraints;\n\n // Methods\n MediaDeviceInfoToConstraints(MediaDeviceInfo: MediaDeviceInfo | null): MediaTrackConstraints;\n selectAudioInputDevice(device: MediaDeviceInfo | null): void;\n selectVideoInputDevice(device: MediaDeviceInfo | null): void;\n selectAudioOutputDevice(device: MediaDeviceInfo | null): void;\n enableDeviceMonitoring(): void;\n disableDeviceMonitoring(): void;\n\n getLocalStream?: () => Promise<MediaStream>;\n}\n\ntype DeviceType = 'microphone' | 'camera' | 'speaker';\n\n@customElement('sw-device-selector')\nexport class DeviceSelector extends LitElement {\n static styles = css`\n :host {\n /* CSS Custom Properties for theming */\n --sw-color-primary: #044cf6;\n --sw-color-primary-hover: #0339c4;\n --sw-color-background: #1a1a1a;\n --sw-color-surface: #2a2a2a;\n --sw-color-surface-hover: #3a3a3a;\n --sw-color-text: #ffffff;\n --sw-color-text-muted: #a0a0a0;\n --sw-color-border: #404040;\n --sw-color-success: #10b981;\n --sw-border-radius: 8px;\n --sw-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n --sw-space-1: 4px;\n --sw-space-2: 8px;\n --sw-space-3: 12px;\n --sw-space-4: 16px;\n --sw-space-6: 24px;\n\n display: block;\n font-family: var(--sw-font-family);\n color: var(--sw-color-text);\n }\n\n .container {\n background: var(--sw-color-background);\n border-radius: var(--sw-border-radius);\n border: 1px solid var(--sw-color-border);\n overflow: hidden;\n min-width: 300px;\n padding: var(--sw-space-4);\n }\n\n .device-section {\n margin-bottom: var(--sw-space-4);\n }\n\n .device-section:last-of-type {\n margin-bottom: 0;\n }\n\n .device-label {\n display: flex;\n align-items: center;\n gap: var(--sw-space-2);\n font-size: 14px;\n font-weight: 500;\n color: var(--sw-color-text);\n margin-bottom: var(--sw-space-2);\n }\n\n .device-icon {\n width: 18px;\n height: 18px;\n flex-shrink: 0;\n color: var(--sw-color-text-muted);\n }\n\n .device-select-wrapper {\n position: relative;\n }\n\n .device-select {\n width: 100%;\n padding: var(--sw-space-3) var(--sw-space-4);\n padding-right: 36px;\n background: var(--sw-color-surface);\n border: 1px solid var(--sw-color-border);\n border-radius: calc(var(--sw-border-radius) - 4px);\n color: var(--sw-color-text);\n font-size: 14px;\n font-family: var(--sw-font-family);\n cursor: pointer;\n appearance: none;\n -webkit-appearance: none;\n -moz-appearance: none;\n transition:\n border-color 0.2s ease,\n background-color 0.2s ease;\n }\n\n .device-select:hover {\n background: var(--sw-color-surface-hover);\n }\n\n .device-select:focus {\n outline: none;\n border-color: var(--sw-color-primary);\n box-shadow: 0 0 0 2px rgba(4, 76, 246, 0.2);\n }\n\n .device-select:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .device-select option {\n background: var(--sw-color-surface);\n color: var(--sw-color-text);\n padding: var(--sw-space-2);\n }\n\n .select-arrow {\n position: absolute;\n right: var(--sw-space-3);\n top: 50%;\n transform: translateY(-50%);\n width: 16px;\n height: 16px;\n color: var(--sw-color-text-muted);\n pointer-events: none;\n }\n\n .no-devices {\n padding: var(--sw-space-3) var(--sw-space-4);\n background: var(--sw-color-surface);\n border: 1px solid var(--sw-color-border);\n border-radius: calc(var(--sw-border-radius) - 4px);\n color: var(--sw-color-text-muted);\n font-size: 14px;\n }\n\n .device-preview {\n margin-top: var(--sw-space-3);\n }\n\n .video-preview {\n width: 100%;\n aspect-ratio: 16/9;\n background: #000;\n border-radius: calc(var(--sw-border-radius) - 4px);\n overflow: hidden;\n }\n\n .video-preview video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n transform: scaleX(-1);\n }\n\n .video-preview-placeholder {\n width: 100%;\n aspect-ratio: 16/9;\n background: var(--sw-color-surface);\n border-radius: calc(var(--sw-border-radius) - 4px);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--sw-color-text-muted);\n font-size: 14px;\n }\n\n .audio-preview {\n display: flex;\n align-items: center;\n gap: var(--sw-space-3);\n padding: var(--sw-space-3);\n background: var(--sw-color-surface);\n border-radius: calc(var(--sw-border-radius) - 4px);\n }\n\n .audio-preview-label {\n font-size: 12px;\n color: var(--sw-color-text-muted);\n flex-shrink: 0;\n }\n\n .audio-level-wrapper {\n flex: 1;\n display: flex;\n justify-content: center;\n }\n\n .test-speaker-btn {\n padding: var(--sw-space-2) var(--sw-space-4);\n background: var(--sw-color-primary);\n color: white;\n border: none;\n border-radius: calc(var(--sw-border-radius) - 4px);\n font-size: 14px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n }\n\n .test-speaker-btn:hover {\n background: var(--sw-color-primary-hover);\n }\n\n .test-speaker-btn:focus {\n outline: none;\n box-shadow:\n 0 0 0 2px var(--sw-color-background),\n 0 0 0 4px var(--sw-color-primary);\n }\n\n .test-speaker-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* Scrollbar styling for select on some browsers */\n .device-select::-webkit-scrollbar {\n width: 8px;\n }\n\n .device-select::-webkit-scrollbar-track {\n background: var(--sw-color-background);\n }\n\n .device-select::-webkit-scrollbar-thumb {\n background: var(--sw-color-border);\n border-radius: 4px;\n }\n\n .device-select::-webkit-scrollbar-thumb:hover {\n background: var(--sw-color-text-muted);\n }\n `;\n\n /**\n * Device controller with observables for device lists\n */\n @property({ type: Object }) deviceController?: DeviceController;\n\n /**\n * Whether to show the preview panel\n */\n @property({ type: Boolean, attribute: 'show-preview' }) showPreview = false;\n\n /**\n * Audio input devices\n */\n @state() private _audioInputDevices: MediaDeviceInfo[] = [];\n\n /**\n * Video input devices\n */\n @state() private _videoInputDevices: MediaDeviceInfo[] = [];\n\n /**\n * Audio output devices\n */\n @state() private _audioOutputDevices: MediaDeviceInfo[] = [];\n\n /**\n * Selected audio input device\n */\n @state() private _selectedAudioInput: MediaDeviceInfo | null = null;\n\n /**\n * Selected video input device\n */\n @state() private _selectedVideoInput: MediaDeviceInfo | null = null;\n\n /**\n * Selected audio output device\n */\n @state() private _selectedAudioOutput: MediaDeviceInfo | null = null;\n\n /**\n * Video preview stream from camera\n */\n @state() private _videoPreviewStream: MediaStream | null = null;\n\n /**\n * Audio preview stream from microphone\n */\n @state() private _audioPreviewStream: MediaStream | null = null;\n\n /**\n * Is test audio playing\n */\n @state() private _isTestingAudio = false;\n\n /**\n * Whether the component is currently visible (combines all visibility factors)\n */\n private _isComponentVisible = false;\n\n /**\n * Whether the component is in the viewport (IntersectionObserver)\n */\n private _isInViewport = false;\n\n /**\n * Whether the document/tab is visible\n */\n private _isDocumentVisible = true;\n\n /**\n * Whether the component is CSS-visible (display, visibility, opacity)\n */\n private _isCSSVisible = true;\n\n /**\n * Video element reference\n */\n private _videoElement?: HTMLVideoElement;\n\n /**\n * RxJS subscriptions for cleanup\n */\n private subscriptions: Subscription[] = [];\n\n /**\n * Audio element for speaker test\n */\n private _testAudioElement?: HTMLAudioElement;\n\n /**\n * IntersectionObserver for viewport visibility\n */\n private _intersectionObserver?: IntersectionObserver;\n\n /**\n * Bound handler for document visibility change\n */\n private _boundHandleDocumentVisibility = this.handleDocumentVisibilityChange.bind(this);\n\n /**\n * Interval ID for CSS visibility polling\n */\n private _cssVisibilityCheckInterval?: ReturnType<typeof setInterval>;\n\n /**\n * Flag to prevent concurrent preview initialization\n */\n private _isInitializingPreviews = false;\n\n /**\n * Interval in milliseconds for checking CSS visibility changes\n */\n private static readonly CSS_VISIBILITY_CHECK_INTERVAL_MS = 200;\n\n /**\n * Lifecycle: Component connected to DOM\n */\n connectedCallback() {\n super.connectedCallback();\n this.setupSubscriptions();\n this.setupVisibilityObservers();\n }\n\n /**\n * Lifecycle: React to property changes\n */\n protected updated(changedProperties: Map<string, unknown>): void {\n super.updated(changedProperties);\n if (changedProperties.has('deviceController')) {\n this.cleanupSubscriptions();\n this.setupSubscriptions();\n }\n if (changedProperties.has('showPreview')) {\n if (this.showPreview && this._isComponentVisible) {\n // Only initialize if component is visible\n this.initializePreviews();\n } else if (!this.showPreview) {\n // Always cleanup when showPreview is disabled\n this.cleanupVideoPreviewStream();\n this.cleanupAudioPreviewStream();\n }\n }\n }\n\n /**\n * Lifecycle: Component disconnected from DOM\n */\n disconnectedCallback() {\n super.disconnectedCallback();\n this.cleanupSubscriptions();\n this.cleanupVisibilityObservers();\n this.cleanupVideoPreviewStream();\n this.cleanupAudioPreviewStream();\n this.stopTestAudio();\n }\n\n /**\n * Setup all visibility observers (IntersectionObserver, document visibility, CSS polling)\n */\n private setupVisibilityObservers(): void {\n // Defensive cleanup in case of multiple calls without disconnect\n this.cleanupVisibilityObservers();\n\n // Setup IntersectionObserver for viewport visibility\n this._intersectionObserver = new IntersectionObserver(\n (entries) => {\n const entry = entries[0];\n if (entry) {\n this._isInViewport = entry.isIntersecting;\n this.updateVisibilityState();\n }\n },\n { threshold: 0 }\n );\n this._intersectionObserver.observe(this);\n\n // Setup document visibility change listener\n this._isDocumentVisible = document.visibilityState === 'visible';\n document.addEventListener('visibilitychange', this._boundHandleDocumentVisibility);\n\n // Setup CSS visibility polling (checks opacity, display, visibility)\n this._isCSSVisible = this.checkCSSVisibility();\n this._cssVisibilityCheckInterval = setInterval(() => {\n const wasVisible = this._isCSSVisible;\n this._isCSSVisible = this.checkCSSVisibility();\n if (wasVisible !== this._isCSSVisible) {\n this.updateVisibilityState();\n }\n }, DeviceSelector.CSS_VISIBILITY_CHECK_INTERVAL_MS);\n }\n\n /**\n * Cleanup all visibility observers\n */\n private cleanupVisibilityObservers(): void {\n if (this._intersectionObserver) {\n this._intersectionObserver.disconnect();\n this._intersectionObserver = undefined;\n }\n\n document.removeEventListener('visibilitychange', this._boundHandleDocumentVisibility);\n\n if (this._cssVisibilityCheckInterval) {\n clearInterval(this._cssVisibilityCheckInterval);\n this._cssVisibilityCheckInterval = undefined;\n }\n }\n\n /**\n * Handle document visibility change (tab switching)\n */\n private handleDocumentVisibilityChange(): void {\n this._isDocumentVisible = document.visibilityState === 'visible';\n this.updateVisibilityState();\n }\n\n /**\n * Check if the component is visible via CSS (display, visibility, opacity)\n * Traverses the DOM tree including shadow DOM boundaries\n */\n private checkCSSVisibility(): boolean {\n // Guard against being called when disconnected\n if (!this.isConnected) {\n return false;\n }\n\n let element: HTMLElement | null = this;\n while (element) {\n const style = getComputedStyle(element);\n if (\n style.display === 'none' ||\n style.visibility === 'hidden' ||\n parseFloat(style.opacity) === 0\n ) {\n return false;\n }\n\n // Handle shadow DOM boundaries by traversing through shadow hosts\n if (element.parentElement) {\n element = element.parentElement;\n } else {\n const root = element.getRootNode();\n element = root instanceof ShadowRoot ? (root.host as HTMLElement) : null;\n }\n }\n return true;\n }\n\n /**\n * Update the combined visibility state and manage preview streams accordingly\n */\n private updateVisibilityState(): void {\n const wasVisible = this._isComponentVisible;\n this._isComponentVisible = this._isInViewport && this._isDocumentVisible && this._isCSSVisible;\n\n // Only react if visibility actually changed\n if (wasVisible === this._isComponentVisible) {\n return;\n }\n\n if (this._isComponentVisible && this.showPreview) {\n // Component became visible - start previews\n this.initializePreviews();\n } else if (!this._isComponentVisible) {\n // Component became invisible - stop previews\n this.cleanupVideoPreviewStream();\n this.cleanupAudioPreviewStream();\n }\n }\n\n /**\n * Subscribe to device controller observables\n */\n private setupSubscriptions(): void {\n if (!this.deviceController) return;\n\n // Subscribe to audio input devices\n this.subscriptions.push(\n this.deviceController.audioInputDevices$.subscribe((devices) => {\n this._audioInputDevices = devices;\n })\n );\n\n // Subscribe to video input devices\n this.subscriptions.push(\n this.deviceController.videoInputDevices$.subscribe((devices) => {\n this._videoInputDevices = devices;\n })\n );\n\n // Subscribe to audio output devices\n this.subscriptions.push(\n this.deviceController.audioOutputDevices$.subscribe((devices) => {\n this._audioOutputDevices = devices;\n })\n );\n\n // Subscribe to selected devices if available\n if (this.deviceController.selectedAudioInputDevice$) {\n this.subscriptions.push(\n this.deviceController.selectedAudioInputDevice$.subscribe((device) => {\n this._selectedAudioInput = device;\n })\n );\n }\n\n if (this.deviceController.selectedVideoInputDevice$) {\n this.subscriptions.push(\n this.deviceController.selectedVideoInputDevice$.subscribe((device) => {\n this._selectedVideoInput = device;\n })\n );\n }\n\n if (this.deviceController.selectedAudioOutputDevice$) {\n this.subscriptions.push(\n this.deviceController.selectedAudioOutputDevice$.subscribe((device) => {\n this._selectedAudioOutput = device;\n })\n );\n }\n }\n\n /**\n * Cleanup all subscriptions\n */\n private cleanupSubscriptions(): void {\n this.subscriptions.forEach((sub) => sub.unsubscribe());\n this.subscriptions = [];\n }\n\n /**\n * Cleanup video preview stream\n * Best practice from SDK: Pause video, clear srcObject, remove tracks from stream, then stop tracks\n * @see https://webrtchacks.com/srcobject-intervention/\n */\n private cleanupVideoPreviewStream(): void {\n // Step 1: Pause and clear the video element\n if (this._videoElement) {\n this._videoElement.pause();\n this._videoElement.srcObject = null;\n this._videoElement = undefined;\n }\n\n // Also check for any video element in shadow DOM (in case _videoElement wasn't set)\n const videoElement = this.shadowRoot?.querySelector('video');\n if (videoElement) {\n videoElement.pause();\n videoElement.srcObject = null;\n }\n\n // Step 2: Remove tracks from stream before stopping (SDK pattern)\n if (this._videoPreviewStream) {\n const tracks = this._videoPreviewStream.getTracks();\n tracks.forEach((track: MediaStreamTrack) => {\n // Remove track from stream first (important for proper release)\n this._videoPreviewStream?.removeTrack(track);\n track.stop();\n });\n this._videoPreviewStream = null;\n }\n }\n\n /**\n * Cleanup audio preview stream\n * Best practice from SDK: Release audio components, remove tracks from stream, then stop tracks\n * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/stop\n */\n private cleanupAudioPreviewStream(): void {\n // Step 1: Release resources from all audio-level components synchronously\n // This closes their AudioContext and disconnects MediaStreamAudioSourceNode\n const audioLevelComponents = this.shadowRoot?.querySelectorAll('sw-audio-level');\n audioLevelComponents?.forEach((component) => {\n const audioLevel = component as HTMLElement & {\n releaseResources?: () => void;\n stream?: MediaStream;\n };\n if (audioLevel.releaseResources) {\n audioLevel.releaseResources();\n } else {\n // Fallback for older versions\n audioLevel.stream = undefined;\n }\n });\n\n // Step 2: Remove tracks from stream before stopping (SDK pattern)\n if (this._audioPreviewStream) {\n const tracks = this._audioPreviewStream.getTracks();\n tracks.forEach((track: MediaStreamTrack) => {\n // Remove track from stream first (important for proper release)\n this._audioPreviewStream?.removeTrack(track);\n track.stop();\n });\n this._audioPreviewStream = null;\n }\n }\n\n /**\n * Get video preview stream from browser API\n */\n private async getVideoPreviewStream(deviceId?: string): Promise<void> {\n this.cleanupVideoPreviewStream();\n\n if (!this.showPreview) return;\n\n try {\n const constraints: MediaStreamConstraints = {\n video: deviceId ? { deviceId: { exact: deviceId } } : true,\n audio: false\n };\n this._videoPreviewStream = await navigator.mediaDevices.getUserMedia(constraints);\n this.updateVideoElement();\n } catch (error) {\n console.warn('Failed to get video preview stream:', error);\n this._videoPreviewStream = null;\n }\n }\n\n /**\n * Get audio preview stream from browser API\n */\n private async getAudioPreviewStream(deviceId?: string): Promise<void> {\n this.cleanupAudioPreviewStream();\n\n if (!this.showPreview) return;\n\n try {\n const constraints: MediaStreamConstraints = {\n video: false,\n audio: deviceId ? { deviceId: { exact: deviceId } } : true\n };\n this._audioPreviewStream = await navigator.mediaDevices.getUserMedia(constraints);\n } catch (error) {\n console.warn('Failed to get audio preview stream:', error);\n this._audioPreviewStream = null;\n }\n }\n\n /**\n * Update video element with current stream\n */\n private updateVideoElement(): void {\n if (this._videoElement && this._videoPreviewStream) {\n this._videoElement.srcObject = this._videoPreviewStream;\n }\n }\n\n /**\n * Initialize previews when show-preview is enabled\n */\n private async initializePreviews(): Promise<void> {\n // Prevent concurrent initialization to avoid stream leaks on rapid visibility toggles\n if (!this.showPreview || this._isInitializingPreviews) return;\n\n this._isInitializingPreviews = true;\n try {\n // Get video preview if we have video devices\n if (this._videoInputDevices.length > 0) {\n const deviceId = this._selectedVideoInput?.deviceId;\n await this.getVideoPreviewStream(deviceId);\n }\n\n // Get audio preview if we have audio devices\n if (this._audioInputDevices.length > 0) {\n const deviceId = this._selectedAudioInput?.deviceId;\n await this.getAudioPreviewStream(deviceId);\n }\n } finally {\n this._isInitializingPreviews = false;\n }\n }\n\n /**\n * Handle device selection change from dropdown\n */\n private handleDeviceChange(event: Event, deviceType: DeviceType): void {\n if (!this.deviceController) return;\n\n const select = event.target as HTMLSelectElement;\n const deviceId = select.value;\n\n let device: MediaDeviceInfo | null = null;\n\n switch (deviceType) {\n case 'microphone':\n device = this._audioInputDevices.find((d) => d.deviceId === deviceId) || null;\n this._selectedAudioInput = device;\n this.deviceController.selectAudioInputDevice?.(device);\n // Update audio preview stream with new device\n if (this.showPreview) {\n this.getAudioPreviewStream(deviceId);\n }\n break;\n case 'camera':\n device = this._videoInputDevices.find((d) => d.deviceId === deviceId) || null;\n this._selectedVideoInput = device;\n this.deviceController.selectVideoInputDevice?.(device);\n // Update video preview stream with new device\n if (this.showPreview) {\n this.getVideoPreviewStream(deviceId);\n }\n break;\n case 'speaker':\n device = this._audioOutputDevices.find((d) => d.deviceId === deviceId) || null;\n this._selectedAudioOutput = device;\n this.deviceController.selectAudioOutputDevice?.(device);\n break;\n }\n\n if (device) {\n this.dispatchEvent(\n new CustomEvent('sw-device-change', {\n detail: { device, deviceType },\n bubbles: true,\n composed: true\n })\n );\n }\n }\n\n /**\n * Test speaker by playing a test tone\n */\n private async testSpeaker(): Promise<void> {\n if (this._isTestingAudio) {\n this.stopTestAudio();\n return;\n }\n\n try {\n this._isTestingAudio = true;\n\n // Create oscillator-based test tone\n const audioContext = new AudioContext();\n const oscillator = audioContext.createOscillator();\n const gainNode = audioContext.createGain();\n\n oscillator.type = 'sine';\n oscillator.frequency.value = 440; // A4 note\n gainNode.gain.value = 0.3;\n\n oscillator.connect(gainNode);\n gainNode.connect(audioContext.destination);\n\n oscillator.start();\n\n // Stop after 1 second\n setTimeout(() => {\n oscillator.stop();\n audioContext.close();\n this._isTestingAudio = false;\n }, 1000);\n\n this.dispatchEvent(\n new CustomEvent('sw-test-speaker', {\n bubbles: true,\n composed: true\n })\n );\n } catch (error) {\n console.error('Failed to play test audio:', error);\n this._isTestingAudio = false;\n }\n }\n\n /**\n * Stop test audio\n */\n private stopTestAudio(): void {\n if (this._testAudioElement) {\n this._testAudioElement.pause();\n this._testAudioElement = undefined;\n }\n this._isTestingAudio = false;\n }\n\n /**\n * Render device icon\n */\n private renderDeviceIcon(deviceType: DeviceType) {\n switch (deviceType) {\n case 'microphone':\n return html`<svg class=\"device-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.91-3c-.49 0-.9.36-.98.85C16.52 14.2 14.47 16 12 16s-4.52-1.8-4.93-4.15c-.08-.49-.49-.85-.98-.85-.61 0-1.09.54-1 1.14.49 3 2.89 5.35 5.91 5.78V20c0 .55.45 1 1 1s1-.45 1-1v-2.08c3.02-.43 5.42-2.78 5.91-5.78.1-.6-.39-1.14-1-1.14z\"\n />\n </svg>`;\n case 'camera':\n return html`<svg class=\"device-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z\"\n />\n </svg>`;\n case 'speaker':\n return html`<svg class=\"device-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\"\n />\n </svg>`;\n }\n }\n\n /**\n * Render dropdown arrow icon\n */\n private renderSelectArrow() {\n return html`<svg class=\"select-arrow\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M7 10l5 5 5-5z\" />\n </svg>`;\n }\n\n /**\n * Render a device selection section\n */\n private renderDeviceSection(\n deviceType: DeviceType,\n label: string,\n devices: MediaDeviceInfo[],\n selectedDevice: MediaDeviceInfo | null\n ) {\n return html`\n <div class=\"device-section\" part=\"device-section\">\n <label class=\"device-label\" for=\"select-${deviceType}\">\n ${this.renderDeviceIcon(deviceType)}\n <span>${label}</span>\n </label>\n ${devices.length === 0\n ? html`<div class=\"no-devices\">No ${label.toLowerCase()} found</div>`\n : html`\n <div class=\"device-select-wrapper\">\n <select\n id=\"select-${deviceType}\"\n class=\"device-select\"\n part=\"device-select\"\n aria-label=\"${label}\"\n .value=${selectedDevice?.deviceId || ''}\n @change=${(e: Event) => this.handleDeviceChange(e, deviceType)}\n >\n ${devices.map(\n (device) => html`\n <option\n value=\"${device.deviceId}\"\n ?selected=${selectedDevice?.deviceId === device.deviceId}\n >\n ${device.label || `Device ${device.deviceId.slice(0, 8)}`}\n </option>\n `\n )}\n </select>\n ${this.renderSelectArrow()}\n </div>\n `}\n </div>\n `;\n }\n\n /**\n * Render video preview for camera section\n */\n private renderVideoPreview() {\n if (!this.showPreview || this._videoInputDevices.length === 0) {\n return nothing;\n }\n\n return html`\n <div class=\"device-preview\">\n ${this._videoPreviewStream\n ? html`\n <div class=\"video-preview\">\n <video\n autoplay\n playsinline\n muted\n .srcObject=${this._videoPreviewStream}\n @loadedmetadata=${(e: Event) => {\n const video = e.target as HTMLVideoElement;\n this._videoElement = video;\n video.play().catch(() => {});\n }}\n ></video>\n </div>\n `\n : html` <div class=\"video-preview-placeholder\">Click to enable camera preview</div> `}\n </div>\n `;\n }\n\n /**\n * Render audio level preview for microphone section\n */\n private renderAudioPreview() {\n if (!this.showPreview || this._audioInputDevices.length === 0) {\n return nothing;\n }\n\n return html`\n <div class=\"device-preview\">\n <div class=\"audio-preview\">\n <span class=\"audio-preview-label\">Level:</span>\n <div class=\"audio-level-wrapper\">\n ${this._audioPreviewStream\n ? html`\n <sw-audio-level\n .stream=${this._audioPreviewStream}\n bars=\"10\"\n orientation=\"horizontal\"\n maxSize=\"20\"\n ></sw-audio-level>\n `\n : html`\n <span style=\"color: var(--sw-color-text-muted); font-size: 12px;\"\n >No audio input</span\n >\n `}\n </div>\n </div>\n </div>\n `;\n }\n\n /**\n * Render the component\n */\n render() {\n return html`\n <div class=\"container\" part=\"container\">\n ${this.renderDeviceSection(\n 'microphone',\n 'Microphone',\n this._audioInputDevices,\n this._selectedAudioInput\n )}\n ${this.renderAudioPreview()}\n ${this.renderDeviceSection(\n 'camera',\n 'Camera',\n this._videoInputDevices,\n this._selectedVideoInput\n )}\n ${this.renderVideoPreview()}\n ${this.renderDeviceSection(\n 'speaker',\n 'Speaker',\n this._audioOutputDevices,\n this._selectedAudioOutput\n )}\n\n <div class=\"device-section\">\n <button\n class=\"test-speaker-btn\"\n @click=${this.testSpeaker}\n ?disabled=${this._isTestingAudio}\n aria-label=\"Test speaker\"\n >\n ${this._isTestingAudio ? 'Playing...' : 'Test Speaker'}\n </button>\n </div>\n </div>\n `;\n }\n}\n\n/**\n * Declare global type for TypeScript\n */\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-device-selector': DeviceSelector;\n }\n}\n"],"names":["DeviceSelector","LitElement","changedProperties","entries","entry","wasVisible","element","style","root","devices","device","sub","videoElement","_a","track","audioLevelComponents","component","audioLevel","deviceId","constraints","error","_b","event","deviceType","d","_d","_c","_f","_e","audioContext","oscillator","gainNode","html","label","selectedDevice","e","nothing","video","css","__decorateClass","property","state","customElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"device-selector.js","sources":["../../src/components/device-selector.ts"],"sourcesContent":["/**\n * Device Selector Component\n *\n * Dropdown-based selector for choosing audio/video devices. Displays device lists from\n * DeviceController observables with labeled dropdowns for microphone, camera, and speaker.\n * Includes inline preview for video (camera) and audio level (microphone) using browser API.\n *\n * @example\n * ```html\n * <sw-device-selector .deviceController=${deviceController} show-preview></sw-device-selector>\n * ```\n */\n\nimport { LitElement, html, css, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { Subscription } from 'rxjs';\nimport type { DeviceController } from '@signalwire/js';\nimport './audio-level.js';\n\ntype DeviceType = 'microphone' | 'camera' | 'speaker';\n\n@customElement('sw-device-selector')\nexport class DeviceSelector extends LitElement {\n static styles = css`\n :host {\n /* CSS Custom Properties for theming */\n --sw-color-primary: #044cf6;\n --sw-color-primary-hover: #0339c4;\n --sw-color-background: #1a1a1a;\n --sw-color-surface: #2a2a2a;\n --sw-color-surface-hover: #3a3a3a;\n --sw-color-text: #ffffff;\n --sw-color-text-muted: #a0a0a0;\n --sw-color-border: #404040;\n --sw-color-success: #10b981;\n --sw-border-radius: 8px;\n --sw-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n --sw-space-1: 4px;\n --sw-space-2: 8px;\n --sw-space-3: 12px;\n --sw-space-4: 16px;\n --sw-space-6: 24px;\n\n display: block;\n font-family: var(--sw-font-family);\n color: var(--sw-color-text);\n }\n\n .container {\n background: var(--sw-color-background);\n border-radius: var(--sw-border-radius);\n border: 1px solid var(--sw-color-border);\n overflow: hidden;\n min-width: 300px;\n padding: var(--sw-space-4);\n }\n\n .device-section {\n margin-bottom: var(--sw-space-4);\n }\n\n .device-section:last-of-type {\n margin-bottom: 0;\n }\n\n .device-label {\n display: flex;\n align-items: center;\n gap: var(--sw-space-2);\n font-size: 14px;\n font-weight: 500;\n color: var(--sw-color-text);\n margin-bottom: var(--sw-space-2);\n }\n\n .device-icon {\n width: 18px;\n height: 18px;\n flex-shrink: 0;\n color: var(--sw-color-text-muted);\n }\n\n .device-select-wrapper {\n position: relative;\n }\n\n .device-select {\n width: 100%;\n padding: var(--sw-space-3) var(--sw-space-4);\n padding-right: 36px;\n background: var(--sw-color-surface);\n border: 1px solid var(--sw-color-border);\n border-radius: calc(var(--sw-border-radius) - 4px);\n color: var(--sw-color-text);\n font-size: 14px;\n font-family: var(--sw-font-family);\n cursor: pointer;\n appearance: none;\n -webkit-appearance: none;\n -moz-appearance: none;\n transition:\n border-color 0.2s ease,\n background-color 0.2s ease;\n }\n\n .device-select:hover {\n background: var(--sw-color-surface-hover);\n }\n\n .device-select:focus {\n outline: none;\n border-color: var(--sw-color-primary);\n box-shadow: 0 0 0 2px rgba(4, 76, 246, 0.2);\n }\n\n .device-select:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .device-select option {\n background: var(--sw-color-surface);\n color: var(--sw-color-text);\n padding: var(--sw-space-2);\n }\n\n .select-arrow {\n position: absolute;\n right: var(--sw-space-3);\n top: 50%;\n transform: translateY(-50%);\n width: 16px;\n height: 16px;\n color: var(--sw-color-text-muted);\n pointer-events: none;\n }\n\n .no-devices {\n padding: var(--sw-space-3) var(--sw-space-4);\n background: var(--sw-color-surface);\n border: 1px solid var(--sw-color-border);\n border-radius: calc(var(--sw-border-radius) - 4px);\n color: var(--sw-color-text-muted);\n font-size: 14px;\n }\n\n .device-preview {\n margin-top: var(--sw-space-3);\n }\n\n .video-preview {\n width: 100%;\n aspect-ratio: 16/9;\n background: #000;\n border-radius: calc(var(--sw-border-radius) - 4px);\n overflow: hidden;\n }\n\n .video-preview video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n transform: scaleX(-1);\n }\n\n .video-preview-placeholder {\n width: 100%;\n aspect-ratio: 16/9;\n background: var(--sw-color-surface);\n border-radius: calc(var(--sw-border-radius) - 4px);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--sw-color-text-muted);\n font-size: 14px;\n }\n\n .audio-preview {\n display: flex;\n align-items: center;\n gap: var(--sw-space-3);\n padding: var(--sw-space-3);\n background: var(--sw-color-surface);\n border-radius: calc(var(--sw-border-radius) - 4px);\n }\n\n .audio-preview-label {\n font-size: 12px;\n color: var(--sw-color-text-muted);\n flex-shrink: 0;\n }\n\n .audio-level-wrapper {\n flex: 1;\n display: flex;\n justify-content: center;\n }\n\n .test-speaker-btn {\n padding: var(--sw-space-2) var(--sw-space-4);\n background: var(--sw-color-primary);\n color: white;\n border: none;\n border-radius: calc(var(--sw-border-radius) - 4px);\n font-size: 14px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n }\n\n .test-speaker-btn:hover {\n background: var(--sw-color-primary-hover);\n }\n\n .test-speaker-btn:focus {\n outline: none;\n box-shadow:\n 0 0 0 2px var(--sw-color-background),\n 0 0 0 4px var(--sw-color-primary);\n }\n\n .test-speaker-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* Scrollbar styling for select on some browsers */\n .device-select::-webkit-scrollbar {\n width: 8px;\n }\n\n .device-select::-webkit-scrollbar-track {\n background: var(--sw-color-background);\n }\n\n .device-select::-webkit-scrollbar-thumb {\n background: var(--sw-color-border);\n border-radius: 4px;\n }\n\n .device-select::-webkit-scrollbar-thumb:hover {\n background: var(--sw-color-text-muted);\n }\n `;\n\n /**\n * Device controller with observables for device lists\n */\n @property({ type: Object }) deviceController?: DeviceController;\n\n /**\n * Whether to show the preview panel\n */\n @property({ type: Boolean, attribute: 'show-preview' }) showPreview = false;\n\n /**\n * Audio input devices\n */\n @state() private _audioInputDevices: MediaDeviceInfo[] = [];\n\n /**\n * Video input devices\n */\n @state() private _videoInputDevices: MediaDeviceInfo[] = [];\n\n /**\n * Audio output devices\n */\n @state() private _audioOutputDevices: MediaDeviceInfo[] = [];\n\n /**\n * Selected audio input device\n */\n @state() private _selectedAudioInput: MediaDeviceInfo | null = null;\n\n /**\n * Selected video input device\n */\n @state() private _selectedVideoInput: MediaDeviceInfo | null = null;\n\n /**\n * Selected audio output device\n */\n @state() private _selectedAudioOutput: MediaDeviceInfo | null = null;\n\n /**\n * Video preview stream from camera\n */\n @state() private _videoPreviewStream: MediaStream | null = null;\n\n /**\n * Audio preview stream from microphone\n */\n @state() private _audioPreviewStream: MediaStream | null = null;\n\n /**\n * Is test audio playing\n */\n @state() private _isTestingAudio = false;\n\n /**\n * Whether the component is currently visible (combines all visibility factors)\n */\n private _isComponentVisible = false;\n\n /**\n * Whether the component is in the viewport (IntersectionObserver)\n */\n private _isInViewport = false;\n\n /**\n * Whether the document/tab is visible\n */\n private _isDocumentVisible = true;\n\n /**\n * Whether the component is CSS-visible (display, visibility, opacity)\n */\n private _isCSSVisible = true;\n\n /**\n * Video element reference\n */\n private _videoElement?: HTMLVideoElement;\n\n /**\n * RxJS subscriptions for cleanup\n */\n private subscriptions: Subscription[] = [];\n\n /**\n * Audio element for speaker test\n */\n private _testAudioElement?: HTMLAudioElement;\n\n /**\n * IntersectionObserver for viewport visibility\n */\n private _intersectionObserver?: IntersectionObserver;\n\n /**\n * Bound handler for document visibility change\n */\n private _boundHandleDocumentVisibility = this.handleDocumentVisibilityChange.bind(this);\n\n /**\n * Interval ID for CSS visibility polling\n */\n private _cssVisibilityCheckInterval?: ReturnType<typeof setInterval>;\n\n /**\n * Flag to prevent concurrent preview initialization\n */\n private _isInitializingPreviews = false;\n\n /**\n * Interval in milliseconds for checking CSS visibility changes\n */\n private static readonly CSS_VISIBILITY_CHECK_INTERVAL_MS = 200;\n\n /**\n * Lifecycle: Component connected to DOM\n */\n connectedCallback() {\n super.connectedCallback();\n this.setupSubscriptions();\n this.setupVisibilityObservers();\n }\n\n /**\n * Lifecycle: React to property changes\n */\n protected updated(changedProperties: Map<string, unknown>): void {\n super.updated(changedProperties);\n if (changedProperties.has('deviceController')) {\n this.cleanupSubscriptions();\n this.setupSubscriptions();\n }\n if (changedProperties.has('showPreview')) {\n if (this.showPreview && this._isComponentVisible) {\n // Only initialize if component is visible\n this.initializePreviews();\n } else if (!this.showPreview) {\n // Always cleanup when showPreview is disabled\n this.cleanupVideoPreviewStream();\n this.cleanupAudioPreviewStream();\n }\n }\n }\n\n /**\n * Lifecycle: Component disconnected from DOM\n */\n disconnectedCallback() {\n super.disconnectedCallback();\n this.cleanupSubscriptions();\n this.cleanupVisibilityObservers();\n this.cleanupVideoPreviewStream();\n this.cleanupAudioPreviewStream();\n this.stopTestAudio();\n }\n\n /**\n * Setup all visibility observers (IntersectionObserver, document visibility, CSS polling)\n */\n private setupVisibilityObservers(): void {\n // Defensive cleanup in case of multiple calls without disconnect\n this.cleanupVisibilityObservers();\n\n // Setup IntersectionObserver for viewport visibility\n this._intersectionObserver = new IntersectionObserver(\n (entries) => {\n const entry = entries[0];\n if (entry) {\n this._isInViewport = entry.isIntersecting;\n this.updateVisibilityState();\n }\n },\n { threshold: 0 }\n );\n this._intersectionObserver.observe(this);\n\n // Setup document visibility change listener\n this._isDocumentVisible = document.visibilityState === 'visible';\n document.addEventListener('visibilitychange', this._boundHandleDocumentVisibility);\n\n // Setup CSS visibility polling (checks opacity, display, visibility)\n this._isCSSVisible = this.checkCSSVisibility();\n this._cssVisibilityCheckInterval = setInterval(() => {\n const wasVisible = this._isCSSVisible;\n this._isCSSVisible = this.checkCSSVisibility();\n if (wasVisible !== this._isCSSVisible) {\n this.updateVisibilityState();\n }\n }, DeviceSelector.CSS_VISIBILITY_CHECK_INTERVAL_MS);\n }\n\n /**\n * Cleanup all visibility observers\n */\n private cleanupVisibilityObservers(): void {\n if (this._intersectionObserver) {\n this._intersectionObserver.disconnect();\n this._intersectionObserver = undefined;\n }\n\n document.removeEventListener('visibilitychange', this._boundHandleDocumentVisibility);\n\n if (this._cssVisibilityCheckInterval) {\n clearInterval(this._cssVisibilityCheckInterval);\n this._cssVisibilityCheckInterval = undefined;\n }\n }\n\n /**\n * Handle document visibility change (tab switching)\n */\n private handleDocumentVisibilityChange(): void {\n this._isDocumentVisible = document.visibilityState === 'visible';\n this.updateVisibilityState();\n }\n\n /**\n * Check if the component is visible via CSS (display, visibility, opacity)\n * Traverses the DOM tree including shadow DOM boundaries\n */\n private checkCSSVisibility(): boolean {\n // Guard against being called when disconnected\n if (!this.isConnected) {\n return false;\n }\n\n let element: HTMLElement | null = this;\n while (element) {\n const style = getComputedStyle(element);\n if (\n style.display === 'none' ||\n style.visibility === 'hidden' ||\n parseFloat(style.opacity) === 0\n ) {\n return false;\n }\n\n // Handle shadow DOM boundaries by traversing through shadow hosts\n if (element.parentElement) {\n element = element.parentElement;\n } else {\n const root = element.getRootNode();\n element = root instanceof ShadowRoot ? (root.host as HTMLElement) : null;\n }\n }\n return true;\n }\n\n /**\n * Update the combined visibility state and manage preview streams accordingly\n */\n private updateVisibilityState(): void {\n const wasVisible = this._isComponentVisible;\n this._isComponentVisible = this._isInViewport && this._isDocumentVisible && this._isCSSVisible;\n\n // Only react if visibility actually changed\n if (wasVisible === this._isComponentVisible) {\n return;\n }\n\n if (this._isComponentVisible && this.showPreview) {\n // Component became visible - start previews\n this.initializePreviews();\n } else if (!this._isComponentVisible) {\n // Component became invisible - stop previews\n this.cleanupVideoPreviewStream();\n this.cleanupAudioPreviewStream();\n }\n }\n\n /**\n * Subscribe to device controller observables\n */\n private setupSubscriptions(): void {\n if (!this.deviceController) return;\n\n // Subscribe to audio input devices\n this.subscriptions.push(\n this.deviceController.audioInputDevices$.subscribe((devices) => {\n this._audioInputDevices = devices;\n })\n );\n\n // Subscribe to video input devices\n this.subscriptions.push(\n this.deviceController.videoInputDevices$.subscribe((devices) => {\n this._videoInputDevices = devices;\n })\n );\n\n // Subscribe to audio output devices\n this.subscriptions.push(\n this.deviceController.audioOutputDevices$.subscribe((devices) => {\n this._audioOutputDevices = devices;\n })\n );\n\n // Subscribe to selected devices if available\n if (this.deviceController.selectedAudioInputDevice$) {\n this.subscriptions.push(\n this.deviceController.selectedAudioInputDevice$.subscribe((device) => {\n this._selectedAudioInput = device;\n })\n );\n }\n\n if (this.deviceController.selectedVideoInputDevice$) {\n this.subscriptions.push(\n this.deviceController.selectedVideoInputDevice$.subscribe((device) => {\n this._selectedVideoInput = device;\n })\n );\n }\n\n if (this.deviceController.selectedAudioOutputDevice$) {\n this.subscriptions.push(\n this.deviceController.selectedAudioOutputDevice$.subscribe((device) => {\n this._selectedAudioOutput = device;\n })\n );\n }\n }\n\n /**\n * Cleanup all subscriptions\n */\n private cleanupSubscriptions(): void {\n this.subscriptions.forEach((sub) => sub.unsubscribe());\n this.subscriptions = [];\n }\n\n /**\n * Cleanup video preview stream\n * Best practice from SDK: Pause video, clear srcObject, remove tracks from stream, then stop tracks\n * @see https://webrtchacks.com/srcobject-intervention/\n */\n private cleanupVideoPreviewStream(): void {\n // Step 1: Pause and clear the video element\n if (this._videoElement) {\n this._videoElement.pause();\n this._videoElement.srcObject = null;\n this._videoElement = undefined;\n }\n\n // Also check for any video element in shadow DOM (in case _videoElement wasn't set)\n const videoElement = this.shadowRoot?.querySelector('video');\n if (videoElement) {\n videoElement.pause();\n videoElement.srcObject = null;\n }\n\n // Step 2: Remove tracks from stream before stopping (SDK pattern)\n if (this._videoPreviewStream) {\n const tracks = this._videoPreviewStream.getTracks();\n tracks.forEach((track: MediaStreamTrack) => {\n // Remove track from stream first (important for proper release)\n this._videoPreviewStream?.removeTrack(track);\n track.stop();\n });\n this._videoPreviewStream = null;\n }\n }\n\n /**\n * Cleanup audio preview stream\n * Best practice from SDK: Release audio components, remove tracks from stream, then stop tracks\n * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/stop\n */\n private cleanupAudioPreviewStream(): void {\n // Step 1: Release resources from all audio-level components synchronously\n // This closes their AudioContext and disconnects MediaStreamAudioSourceNode\n const audioLevelComponents = this.shadowRoot?.querySelectorAll('sw-audio-level');\n audioLevelComponents?.forEach((component) => {\n const audioLevel = component as HTMLElement & {\n releaseResources?: () => void;\n stream?: MediaStream;\n };\n if (audioLevel.releaseResources) {\n audioLevel.releaseResources();\n } else {\n // Fallback for older versions\n audioLevel.stream = undefined;\n }\n });\n\n // Step 2: Remove tracks from stream before stopping (SDK pattern)\n if (this._audioPreviewStream) {\n const tracks = this._audioPreviewStream.getTracks();\n tracks.forEach((track: MediaStreamTrack) => {\n // Remove track from stream first (important for proper release)\n this._audioPreviewStream?.removeTrack(track);\n track.stop();\n });\n this._audioPreviewStream = null;\n }\n }\n\n /**\n * Get video preview stream from browser API\n */\n private async getVideoPreviewStream(deviceId?: string): Promise<void> {\n this.cleanupVideoPreviewStream();\n\n if (!this.showPreview) return;\n\n try {\n const constraints: MediaStreamConstraints = {\n video: deviceId ? { deviceId: { exact: deviceId } } : true,\n audio: false\n };\n this._videoPreviewStream = await navigator.mediaDevices.getUserMedia(constraints);\n this.updateVideoElement();\n } catch (error) {\n console.warn('Failed to get video preview stream:', error);\n this._videoPreviewStream = null;\n }\n }\n\n /**\n * Get audio preview stream from browser API\n */\n private async getAudioPreviewStream(deviceId?: string): Promise<void> {\n this.cleanupAudioPreviewStream();\n\n if (!this.showPreview) return;\n\n try {\n const constraints: MediaStreamConstraints = {\n video: false,\n audio: deviceId ? { deviceId: { exact: deviceId } } : true\n };\n this._audioPreviewStream = await navigator.mediaDevices.getUserMedia(constraints);\n } catch (error) {\n console.warn('Failed to get audio preview stream:', error);\n this._audioPreviewStream = null;\n }\n }\n\n /**\n * Update video element with current stream\n */\n private updateVideoElement(): void {\n if (this._videoElement && this._videoPreviewStream) {\n this._videoElement.srcObject = this._videoPreviewStream;\n }\n }\n\n /**\n * Initialize previews when show-preview is enabled\n */\n private async initializePreviews(): Promise<void> {\n // Prevent concurrent initialization to avoid stream leaks on rapid visibility toggles\n if (!this.showPreview || this._isInitializingPreviews) return;\n\n this._isInitializingPreviews = true;\n try {\n // Get video preview if we have video devices\n if (this._videoInputDevices.length > 0) {\n const deviceId = this._selectedVideoInput?.deviceId;\n await this.getVideoPreviewStream(deviceId);\n }\n\n // Get audio preview if we have audio devices\n if (this._audioInputDevices.length > 0) {\n const deviceId = this._selectedAudioInput?.deviceId;\n await this.getAudioPreviewStream(deviceId);\n }\n } finally {\n this._isInitializingPreviews = false;\n }\n }\n\n /**\n * Handle device selection change from dropdown\n */\n private handleDeviceChange(event: Event, deviceType: DeviceType): void {\n if (!this.deviceController) return;\n\n const select = event.target as HTMLSelectElement;\n const deviceId = select.value;\n\n let device: MediaDeviceInfo | null = null;\n\n switch (deviceType) {\n case 'microphone':\n device = this._audioInputDevices.find((d) => d.deviceId === deviceId) || null;\n this._selectedAudioInput = device;\n this.deviceController.selectAudioInputDevice?.(device);\n // Update audio preview stream with new device\n if (this.showPreview) {\n this.getAudioPreviewStream(deviceId);\n }\n break;\n case 'camera':\n device = this._videoInputDevices.find((d) => d.deviceId === deviceId) || null;\n this._selectedVideoInput = device;\n this.deviceController.selectVideoInputDevice?.(device);\n // Update video preview stream with new device\n if (this.showPreview) {\n this.getVideoPreviewStream(deviceId);\n }\n break;\n case 'speaker':\n device = this._audioOutputDevices.find((d) => d.deviceId === deviceId) || null;\n this._selectedAudioOutput = device;\n this.deviceController.selectAudioOutputDevice?.(device);\n break;\n }\n\n if (device) {\n this.dispatchEvent(\n new CustomEvent('sw-device-change', {\n detail: { device, deviceType },\n bubbles: true,\n composed: true\n })\n );\n }\n }\n\n /**\n * Test speaker by playing a test tone\n */\n private async testSpeaker(): Promise<void> {\n if (this._isTestingAudio) {\n this.stopTestAudio();\n return;\n }\n\n try {\n this._isTestingAudio = true;\n\n // Create oscillator-based test tone\n const audioContext = new AudioContext();\n const oscillator = audioContext.createOscillator();\n const gainNode = audioContext.createGain();\n\n oscillator.type = 'sine';\n oscillator.frequency.value = 440; // A4 note\n gainNode.gain.value = 0.3;\n\n oscillator.connect(gainNode);\n gainNode.connect(audioContext.destination);\n\n oscillator.start();\n\n // Stop after 1 second\n setTimeout(() => {\n oscillator.stop();\n audioContext.close();\n this._isTestingAudio = false;\n }, 1000);\n\n this.dispatchEvent(\n new CustomEvent('sw-test-speaker', {\n bubbles: true,\n composed: true\n })\n );\n } catch (error) {\n console.error('Failed to play test audio:', error);\n this._isTestingAudio = false;\n }\n }\n\n /**\n * Stop test audio\n */\n private stopTestAudio(): void {\n if (this._testAudioElement) {\n this._testAudioElement.pause();\n this._testAudioElement = undefined;\n }\n this._isTestingAudio = false;\n }\n\n /**\n * Render device icon\n */\n private renderDeviceIcon(deviceType: DeviceType) {\n switch (deviceType) {\n case 'microphone':\n return html`<svg class=\"device-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.91-3c-.49 0-.9.36-.98.85C16.52 14.2 14.47 16 12 16s-4.52-1.8-4.93-4.15c-.08-.49-.49-.85-.98-.85-.61 0-1.09.54-1 1.14.49 3 2.89 5.35 5.91 5.78V20c0 .55.45 1 1 1s1-.45 1-1v-2.08c3.02-.43 5.42-2.78 5.91-5.78.1-.6-.39-1.14-1-1.14z\"\n />\n </svg>`;\n case 'camera':\n return html`<svg class=\"device-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z\"\n />\n </svg>`;\n case 'speaker':\n return html`<svg class=\"device-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\"\n />\n </svg>`;\n }\n }\n\n /**\n * Render dropdown arrow icon\n */\n private renderSelectArrow() {\n return html`<svg class=\"select-arrow\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M7 10l5 5 5-5z\" />\n </svg>`;\n }\n\n /**\n * Render a device selection section\n */\n private renderDeviceSection(\n deviceType: DeviceType,\n label: string,\n devices: MediaDeviceInfo[],\n selectedDevice: MediaDeviceInfo | null\n ) {\n return html`\n <div class=\"device-section\" part=\"device-section\">\n <label class=\"device-label\" for=\"select-${deviceType}\">\n ${this.renderDeviceIcon(deviceType)}\n <span>${label}</span>\n </label>\n ${devices.length === 0\n ? html`<div class=\"no-devices\">No ${label.toLowerCase()} found</div>`\n : html`\n <div class=\"device-select-wrapper\">\n <select\n id=\"select-${deviceType}\"\n class=\"device-select\"\n part=\"device-select\"\n aria-label=\"${label}\"\n .value=${selectedDevice?.deviceId || ''}\n @change=${(e: Event) => this.handleDeviceChange(e, deviceType)}\n >\n ${devices.map(\n (device) => html`\n <option\n value=\"${device.deviceId}\"\n ?selected=${selectedDevice?.deviceId === device.deviceId}\n >\n ${device.label || `Device ${device.deviceId.slice(0, 8)}`}\n </option>\n `\n )}\n </select>\n ${this.renderSelectArrow()}\n </div>\n `}\n </div>\n `;\n }\n\n /**\n * Render video preview for camera section\n */\n private renderVideoPreview() {\n if (!this.showPreview || this._videoInputDevices.length === 0) {\n return nothing;\n }\n\n return html`\n <div class=\"device-preview\">\n ${this._videoPreviewStream\n ? html`\n <div class=\"video-preview\">\n <video\n autoplay\n playsinline\n muted\n .srcObject=${this._videoPreviewStream}\n @loadedmetadata=${(e: Event) => {\n const video = e.target as HTMLVideoElement;\n this._videoElement = video;\n video.play().catch(() => {});\n }}\n ></video>\n </div>\n `\n : html` <div class=\"video-preview-placeholder\">Click to enable camera preview</div> `}\n </div>\n `;\n }\n\n /**\n * Render audio level preview for microphone section\n */\n private renderAudioPreview() {\n if (!this.showPreview || this._audioInputDevices.length === 0) {\n return nothing;\n }\n\n return html`\n <div class=\"device-preview\">\n <div class=\"audio-preview\">\n <span class=\"audio-preview-label\">Level:</span>\n <div class=\"audio-level-wrapper\">\n ${this._audioPreviewStream\n ? html`\n <sw-audio-level\n .stream=${this._audioPreviewStream}\n bars=\"10\"\n orientation=\"horizontal\"\n maxSize=\"20\"\n ></sw-audio-level>\n `\n : html`\n <span style=\"color: var(--sw-color-text-muted); font-size: 12px;\"\n >No audio input</span\n >\n `}\n </div>\n </div>\n </div>\n `;\n }\n\n /**\n * Render the component\n */\n render() {\n return html`\n <div class=\"container\" part=\"container\">\n ${this.renderDeviceSection(\n 'microphone',\n 'Microphone',\n this._audioInputDevices,\n this._selectedAudioInput\n )}\n ${this.renderAudioPreview()}\n ${this.renderDeviceSection(\n 'camera',\n 'Camera',\n this._videoInputDevices,\n this._selectedVideoInput\n )}\n ${this.renderVideoPreview()}\n ${this.renderDeviceSection(\n 'speaker',\n 'Speaker',\n this._audioOutputDevices,\n this._selectedAudioOutput\n )}\n\n <div class=\"device-section\">\n <button\n class=\"test-speaker-btn\"\n @click=${this.testSpeaker}\n ?disabled=${this._isTestingAudio}\n aria-label=\"Test speaker\"\n >\n ${this._isTestingAudio ? 'Playing...' : 'Test Speaker'}\n </button>\n </div>\n </div>\n `;\n }\n}\n\n/**\n * Declare global type for TypeScript\n */\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-device-selector': DeviceSelector;\n }\n}\n"],"names":["DeviceSelector","LitElement","changedProperties","entries","entry","wasVisible","element","style","root","devices","device","sub","videoElement","_a","track","audioLevelComponents","component","audioLevel","deviceId","constraints","error","_b","event","deviceType","d","_d","_c","_f","_e","audioContext","oscillator","gainNode","html","label","selectedDevice","e","nothing","video","css","__decorateClass","property","state","customElement"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { DialpadComponent } from './components/dialpad.js';
|
|
|
14
14
|
export { ClickToCallComponent } from './components/click-to-call.js';
|
|
15
15
|
export { DirectoryComponent } from './components/directory.js';
|
|
16
16
|
export { ParticipantControlsComponent } from './components/participant-controls.js';
|
|
17
|
-
export
|
|
17
|
+
export type { Call, CallSelf, DeviceController, LayoutLayer, Participant } from './types/index.js';
|
|
18
18
|
export * from './context/index.js';
|
|
19
19
|
export { html, css, LitElement } from 'lit';
|
|
20
20
|
export type { TemplateResult, CSSResult } from 'lit';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AAGpF,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AAGpF,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGnG,cAAc,oBAAoB,CAAC;AAGnC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAC5C,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAQrD;;GAEG;AACH,eAAO,MAAM,OAAO,EAAE,MAAoB,CAAC;AAE3C;;GAEG;AACH,eAAO,MAAM,KAAK,EAAE,OAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { ExampleButton as m } from "./components/example-button.js";
|
|
2
|
-
import { CallMedia as
|
|
2
|
+
import { CallMedia as l } from "./components/call-media.js";
|
|
3
3
|
import { Participants as f } from "./components/participants.js";
|
|
4
|
-
import { SelfMedia as
|
|
5
|
-
import { AudioLevel as
|
|
4
|
+
import { SelfMedia as c } from "./components/self-media.js";
|
|
5
|
+
import { AudioLevel as C } from "./components/audio-level.js";
|
|
6
6
|
import { DeviceSelector as v } from "./components/device-selector.js";
|
|
7
7
|
import { CallControls as u } from "./components/call-controls.js";
|
|
8
8
|
import { CallStatusComponent as E } from "./components/call-status.js";
|
|
9
|
-
import { DialpadComponent as
|
|
10
|
-
import { ClickToCallComponent as
|
|
11
|
-
import { DirectoryComponent as
|
|
12
|
-
import { ParticipantControlsComponent as
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { callContext as z } from "./context/call-context.js";
|
|
9
|
+
import { DialpadComponent as S } from "./components/dialpad.js";
|
|
10
|
+
import { ClickToCallComponent as L } from "./components/click-to-call.js";
|
|
11
|
+
import { DirectoryComponent as P } from "./components/directory.js";
|
|
12
|
+
import { ParticipantControlsComponent as g } from "./components/participant-controls.js";
|
|
13
|
+
import { LitElement as A, css as B, html as R } from "lit";
|
|
14
|
+
import { callContext as j } from "./context/call-context.js";
|
|
16
15
|
const t = "1.0.0-rc.0", r = !0, e = () => {
|
|
17
16
|
if (typeof window < "u") {
|
|
18
17
|
const o = new CustomEvent("signalwire:web-components:ready", {
|
|
@@ -23,24 +22,22 @@ const t = "1.0.0-rc.0", r = !0, e = () => {
|
|
|
23
22
|
};
|
|
24
23
|
e();
|
|
25
24
|
export {
|
|
26
|
-
|
|
25
|
+
C as AudioLevel,
|
|
27
26
|
u as CallControls,
|
|
28
|
-
|
|
27
|
+
l as CallMedia,
|
|
29
28
|
E as CallStatusComponent,
|
|
30
|
-
|
|
29
|
+
L as ClickToCallComponent,
|
|
31
30
|
v as DeviceSelector,
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
S as DialpadComponent,
|
|
32
|
+
P as DirectoryComponent,
|
|
34
33
|
m as ExampleButton,
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
A as LitElement,
|
|
35
|
+
g as ParticipantControlsComponent,
|
|
37
36
|
f as Participants,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
B as getSelfId,
|
|
43
|
-
j as html,
|
|
37
|
+
c as SelfMedia,
|
|
38
|
+
j as callContext,
|
|
39
|
+
B as css,
|
|
40
|
+
R as html,
|
|
44
41
|
r as ready,
|
|
45
42
|
t as version
|
|
46
43
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * @signalwire/web-components\n * UI Components Library built with Lit\n */\n\n// Export components\nexport { ExampleButton } from './components/example-button.js';\nexport { CallMedia } from './components/call-media.js';\nexport { Participants } from './components/participants.js';\nexport { SelfMedia } from './components/self-media.js';\nexport { AudioLevel } from './components/audio-level.js';\nexport { DeviceSelector } from './components/device-selector.js';\nexport { CallControls } from './components/call-controls.js';\nexport { CallStatusComponent } from './components/call-status.js';\nexport { DialpadComponent } from './components/dialpad.js';\nexport { ClickToCallComponent } from './components/click-to-call.js';\nexport { DirectoryComponent } from './components/directory.js';\nexport { ParticipantControlsComponent } from './components/participant-controls.js';\n\n// Export types (re-exported from core)\nexport
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * @signalwire/web-components\n * UI Components Library built with Lit\n */\n\n// Export components\nexport { ExampleButton } from './components/example-button.js';\nexport { CallMedia } from './components/call-media.js';\nexport { Participants } from './components/participants.js';\nexport { SelfMedia } from './components/self-media.js';\nexport { AudioLevel } from './components/audio-level.js';\nexport { DeviceSelector } from './components/device-selector.js';\nexport { CallControls } from './components/call-controls.js';\nexport { CallStatusComponent } from './components/call-status.js';\nexport { DialpadComponent } from './components/dialpad.js';\nexport { ClickToCallComponent } from './components/click-to-call.js';\nexport { DirectoryComponent } from './components/directory.js';\nexport { ParticipantControlsComponent } from './components/participant-controls.js';\n\n// Export types (re-exported from core)\nexport type { Call, CallSelf, DeviceController, LayoutLayer, Participant } from './types/index.js';\n\n// Export context\nexport * from './context/index.js';\n\n// Re-export Lit utilities for convenience\nexport { html, css, LitElement } from 'lit';\nexport type { TemplateResult, CSSResult } from 'lit';\n\n// ============================================================================\n// Library Ready Event (for async/dynamic script loading)\n// ============================================================================\n\ndeclare const __VERSION__: string;\n\n/**\n * Library version from package.json, injected at build time.\n */\nexport const version: string = __VERSION__;\n\n/**\n * Flag indicating the library has been loaded and is ready to use.\n */\nexport const ready: boolean = true;\n\n/**\n * Emits 'signalwire:web-components:ready' event when the library is loaded.\n *\n * Scripts that might load BEFORE the library (check flag first):\n * ```js\n * if (window.SignalWireUI?.ready) {\n * // Library already loaded, use it directly\n * initApp();\n * } else {\n * window.addEventListener('signalwire:web-components:ready', () => initApp());\n * }\n * ```\n */\nconst emitReadyEvent = (): void => {\n if (typeof window !== 'undefined') {\n const event = new CustomEvent('signalwire:web-components:ready', {\n detail: { version: __VERSION__ }\n });\n window.dispatchEvent(event);\n }\n};\n\nemitReadyEvent();\n"],"names":["version","ready","emitReadyEvent","event"],"mappings":";;;;;;;;;;;;;;AAsCO,MAAMA,IAAkB,cAKlBC,IAAiB,IAexBC,IAAiB,MAAY;AACjC,MAAI,OAAO,SAAW,KAAa;AACjC,UAAMC,IAAQ,IAAI,YAAY,mCAAmC;AAAA,MAC/D,QAAQ,EAAE,SAAS,aAAA;AAAA,IAAY,CAChC;AACD,WAAO,cAAcA,CAAK;AAAA,EAC5B;AACF;AAEAD,EAAA;"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Other types are defined locally for web-component specific needs.
|
|
6
6
|
*/
|
|
7
7
|
import type { Observable } from 'rxjs';
|
|
8
|
-
import type { Call, LayoutLayer } from '@signalwire/js';
|
|
9
|
-
export type { Call, LayoutLayer };
|
|
8
|
+
import type { Call, DeviceController, LayoutLayer } from '@signalwire/js';
|
|
9
|
+
export type { Call, DeviceController, LayoutLayer };
|
|
10
10
|
/**
|
|
11
11
|
* Type helper for accessing call.self with proper typing
|
|
12
12
|
* The SDK's Call.self is typed as unknown for flexibility
|
|
@@ -41,27 +41,4 @@ export interface Participant {
|
|
|
41
41
|
unmuteVideo?(): Promise<void>;
|
|
42
42
|
remove?(): Promise<void>;
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Device info interface matching MediaDeviceInfo structure
|
|
46
|
-
*/
|
|
47
|
-
export interface DeviceInfo {
|
|
48
|
-
deviceId: string;
|
|
49
|
-
label: string;
|
|
50
|
-
kind: 'audioinput' | 'audiooutput' | 'videoinput';
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* DeviceController interface for observing device lists
|
|
54
|
-
*/
|
|
55
|
-
export interface DeviceController {
|
|
56
|
-
audioInputDevices$: Observable<DeviceInfo[]>;
|
|
57
|
-
videoInputDevices$: Observable<DeviceInfo[]>;
|
|
58
|
-
audioOutputDevices$: Observable<DeviceInfo[]>;
|
|
59
|
-
selectedAudioInputDevice$?: Observable<DeviceInfo | null>;
|
|
60
|
-
selectedVideoInputDevice$?: Observable<DeviceInfo | null>;
|
|
61
|
-
selectedAudioOutputDevice$?: Observable<DeviceInfo | null>;
|
|
62
|
-
selectAudioInputDevice?: (device: DeviceInfo) => void;
|
|
63
|
-
selectVideoInputDevice?: (device: DeviceInfo) => void;
|
|
64
|
-
selectAudioOutputDevice?: (device: DeviceInfo) => void;
|
|
65
|
-
getLocalStream?: () => Promise<MediaStream>;
|
|
66
|
-
}
|
|
67
44
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG1E,YAAY,EAAE,IAAI,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC;AAEpD;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGpE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,WAAW,EAAE,CAEvE;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,SAAS,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B"}
|
package/dist/types/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/types/index.ts"],"sourcesContent":["/**\n * Type definitions for call-media components.\n *\n * Call and LayoutLayer types are imported from the SDK.\n * Other types are defined locally for web-component specific needs.\n */\n\nimport type { Observable } from 'rxjs';\nimport type { Call, LayoutLayer } from '@signalwire/js';\n\n// Re-export SDK types for internal use\nexport type { Call, LayoutLayer };\n\n/**\n * Type helper for accessing call.self with proper typing\n * The SDK's Call.self is typed as unknown for flexibility\n */\nexport interface CallSelf {\n id: string;\n}\n\n/**\n * Helper to safely get self ID from a Call\n */\nexport function getSelfId(call: Call | undefined): string | undefined {\n const self = call?.self as CallSelf | undefined;\n return self?.id;\n}\n\n/**\n * Helper to cast participants from unknown[] to Participant[]\n * The SDK uses unknown[] for flexibility, but web-components knows the shape\n */\nexport function castParticipants(participants: unknown[]): Participant[] {\n return participants as Participant[];\n}\n\n/**\n * Participant interface for call participants\n * Web-component specific with optional methods\n */\nexport interface Participant {\n id: string;\n name?: string;\n name$?: Observable<string>;\n audioMuted?: boolean;\n audioMuted$?: Observable<boolean>;\n videoMuted?: boolean;\n videoMuted$?: Observable<boolean>;\n mute?(): Promise<void>;\n unmute?(): Promise<void>;\n muteVideo?(): Promise<void>;\n unmuteVideo?(): Promise<void>;\n remove?(): Promise<void>;\n}\n\n
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/types/index.ts"],"sourcesContent":["/**\n * Type definitions for call-media components.\n *\n * Call and LayoutLayer types are imported from the SDK.\n * Other types are defined locally for web-component specific needs.\n */\n\nimport type { Observable } from 'rxjs';\nimport type { Call, DeviceController, LayoutLayer } from '@signalwire/js';\n\n// Re-export SDK types for internal use\nexport type { Call, DeviceController, LayoutLayer };\n\n/**\n * Type helper for accessing call.self with proper typing\n * The SDK's Call.self is typed as unknown for flexibility\n */\nexport interface CallSelf {\n id: string;\n}\n\n/**\n * Helper to safely get self ID from a Call\n */\nexport function getSelfId(call: Call | undefined): string | undefined {\n const self = call?.self as CallSelf | undefined;\n return self?.id;\n}\n\n/**\n * Helper to cast participants from unknown[] to Participant[]\n * The SDK uses unknown[] for flexibility, but web-components knows the shape\n */\nexport function castParticipants(participants: unknown[]): Participant[] {\n return participants as Participant[];\n}\n\n/**\n * Participant interface for call participants\n * Web-component specific with optional methods\n */\nexport interface Participant {\n id: string;\n name?: string;\n name$?: Observable<string>;\n audioMuted?: boolean;\n audioMuted$?: Observable<boolean>;\n videoMuted?: boolean;\n videoMuted$?: Observable<boolean>;\n mute?(): Promise<void>;\n unmute?(): Promise<void>;\n muteVideo?(): Promise<void>;\n unmuteVideo?(): Promise<void>;\n remove?(): Promise<void>;\n}\n\n"],"names":["getSelfId","call","self","castParticipants","participants"],"mappings":"AAwBO,SAASA,EAAUC,GAA4C;AACpE,QAAMC,IAAOD,KAAA,gBAAAA,EAAM;AACnB,SAAOC,KAAA,gBAAAA,EAAM;AACf;AAMO,SAASC,EAAiBC,GAAwC;AACvE,SAAOA;AACT;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signalwire/web-components",
|
|
3
|
-
"version": "1.0.0-dev-
|
|
3
|
+
"version": "1.0.0-dev-20260318131609",
|
|
4
4
|
"description": "UI components library built with Lit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"rxjs": "^7.8.2"
|
|
116
116
|
},
|
|
117
117
|
"peerDependencies": {
|
|
118
|
-
"@signalwire/js": "4.0.0-dev-
|
|
118
|
+
"@signalwire/js": "4.0.0-dev-20260318131609"
|
|
119
119
|
},
|
|
120
120
|
"devDependencies": {
|
|
121
121
|
"@playwright/test": "^1.56.1",
|