@livepeer-frameworks/streamcrafter-wc 0.1.0 → 0.1.2

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.
@@ -1,103 +1,484 @@
1
1
  import { __decorate } from '../node_modules/.pnpm/@rollup_plugin-typescript@12.3.0_rollup@4.57.1_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js';
2
- import { css, LitElement, html, nothing } from 'lit';
2
+ import { css, LitElement, nothing, html } from 'lit';
3
3
  import { property, state, customElement } from 'lit/decorators.js';
4
4
  import { classMap } from 'lit/directives/class-map.js';
5
5
  import { sharedStyles } from '../styles/shared-styles.js';
6
6
  import { utilityStyles } from '../styles/utility-styles.js';
7
7
  import { xIcon } from '../icons/index.js';
8
8
 
9
+ function formatBitrate(bps) {
10
+ if (bps >= 1_000_000)
11
+ return `${(bps / 1_000_000).toFixed(1)} Mbps`;
12
+ return `${(bps / 1000).toFixed(0)} kbps`;
13
+ }
9
14
  let FwScAdvanced = class FwScAdvanced extends LitElement {
10
15
  constructor() {
11
16
  super(...arguments);
12
- this._activeTab = "stats";
17
+ this.compositorEnabled = false;
18
+ this.compositorRendererType = null;
19
+ this.compositorStats = null;
20
+ this.sceneCount = 0;
21
+ this.layerCount = 0;
22
+ this._activeTab = "audio";
13
23
  }
14
24
  render() {
15
25
  return html `
16
26
  <div class="panel">
17
27
  <div class="header">
18
- <div class="tabs">
19
- <button
20
- class=${classMap({ tab: true, "tab--active": this._activeTab === "stats" })}
21
- @click=${() => {
28
+ <button
29
+ class=${classMap({ tab: true, "tab--active": this._activeTab === "audio" })}
30
+ @click=${() => {
31
+ this._activeTab = "audio";
32
+ }}
33
+ >
34
+ Audio
35
+ </button>
36
+ <button
37
+ class=${classMap({ tab: true, "tab--active": this._activeTab === "stats" })}
38
+ @click=${() => {
22
39
  this._activeTab = "stats";
23
40
  }}
24
- >
25
- Stats
26
- </button>
27
- <button
28
- class=${classMap({ tab: true, "tab--active": this._activeTab === "info" })}
29
- @click=${() => {
41
+ >
42
+ Stats
43
+ </button>
44
+ <button
45
+ class=${classMap({ tab: true, "tab--active": this._activeTab === "info" })}
46
+ @click=${() => {
30
47
  this._activeTab = "info";
31
48
  }}
32
- >
33
- Info
34
- </button>
35
- </div>
49
+ >
50
+ Info
51
+ </button>
52
+ ${this.compositorEnabled
53
+ ? html `
54
+ <button
55
+ class=${classMap({
56
+ tab: true,
57
+ "tab--active": this._activeTab === "compositor",
58
+ })}
59
+ @click=${() => {
60
+ this._activeTab = "compositor";
61
+ }}
62
+ >
63
+ Comp
64
+ </button>
65
+ `
66
+ : nothing}
67
+ <div style="flex:1"></div>
36
68
  <button
37
69
  class="close"
38
70
  @click=${() => this.dispatchEvent(new CustomEvent("fw-close", { bubbles: true, composed: true }))}
39
- aria-label="Close panel"
71
+ aria-label="Close advanced panel"
40
72
  >
41
- ${xIcon(14)}
73
+ ${xIcon(12)}
42
74
  </button>
43
75
  </div>
44
76
  <div class="body">
45
- ${this._activeTab === "stats" ? this._renderStats() : this._renderInfo()}
77
+ ${this._activeTab === "audio"
78
+ ? this._renderAudio()
79
+ : this._activeTab === "stats"
80
+ ? this._renderStats()
81
+ : this._activeTab === "info"
82
+ ? this._renderInfo()
83
+ : this._renderCompositor()}
46
84
  </div>
47
85
  </div>
48
86
  `;
49
87
  }
88
+ // ---- Audio Tab ----
89
+ _renderAudio() {
90
+ const s = this.ic.s;
91
+ const masterVolume = this.ic.getMasterVolume();
92
+ const audioLevel = s.audioLevel;
93
+ const levelColor = audioLevel > 0.9 ? "#f7768e" : audioLevel > 0.7 ? "#e0af68" : "#9ece6a";
94
+ const volColor = masterVolume > 1 ? "#e0af68" : masterVolume === 1 ? "#9ece6a" : "#c0caf5";
95
+ return html `
96
+ <!-- Master Volume -->
97
+ <div class="section">
98
+ <div class="section-header">Master Volume</div>
99
+ <div style="display:flex;align-items:center;gap:12px">
100
+ <fw-sc-volume
101
+ .value=${masterVolume}
102
+ .min=${0}
103
+ .max=${2}
104
+ @fw-sc-volume-change=${(e) => this.ic.setMasterVolume(e.detail.value)}
105
+ ></fw-sc-volume>
106
+ <span
107
+ style="font-size:14px;min-width:48px;text-align:right;color:${volColor}"
108
+ >
109
+ ${Math.round(masterVolume * 100)}%
110
+ </span>
111
+ </div>
112
+ ${masterVolume > 1
113
+ ? html `<div style="font-size:10px;color:#e0af68;margin-top:4px">
114
+ +${((masterVolume - 1) * 100).toFixed(0)}% boost
115
+ </div>`
116
+ : nothing}
117
+ </div>
118
+
119
+ <!-- Audio Level -->
120
+ <div class="section">
121
+ <div class="section-header">Output Level</div>
122
+ <div class="level-bar">
123
+ <div
124
+ class="level-fill"
125
+ style="width:${audioLevel * 100}%;background:${levelColor}"
126
+ ></div>
127
+ </div>
128
+ <div class="level-labels">
129
+ <span>-60dB</span><span>0dB</span>
130
+ </div>
131
+ </div>
132
+
133
+ <!-- Audio Mixing Status -->
134
+ <div class="section">
135
+ <div style="display:flex;justify-content:space-between;align-items:center">
136
+ <span class="section-header" style="margin-bottom:0">Audio Mixing</span>
137
+ <span
138
+ class="badge"
139
+ style="background:rgba(158,206,106,0.2);color:#9ece6a"
140
+ >
141
+ ON
142
+ </span>
143
+ </div>
144
+ <div style="font-size:10px;color:#565f89;margin-top:4px">
145
+ Compressor + Limiter active
146
+ </div>
147
+ </div>
148
+
149
+ <!-- Audio Processing -->
150
+ <div style="border-bottom:1px solid rgba(65,72,104,0.3)">
151
+ <div class="section-dark">
152
+ <span class="section-header" style="margin-bottom:0">Processing</span>
153
+ <span style="font-size:9px;color:#565f89">
154
+ profile: ${s.qualityProfile}
155
+ </span>
156
+ </div>
157
+ ${this._renderToggle("Echo Cancellation", true)}
158
+ ${this._renderToggle("Noise Suppression", true)}
159
+ ${this._renderToggle("Auto Gain Control", true)}
160
+ </div>
161
+ `;
162
+ }
163
+ _renderToggle(label, checked) {
164
+ return html `
165
+ <div class="processing-row">
166
+ <span class="processing-label">${label}</span>
167
+ <button
168
+ type="button"
169
+ role="switch"
170
+ aria-checked=${checked}
171
+ class="toggle ${checked ? "toggle--on" : "toggle--off"}"
172
+ >
173
+ <div class="toggle-knob"></div>
174
+ </button>
175
+ </div>
176
+ `;
177
+ }
178
+ // ---- Stats Tab ----
50
179
  _renderStats() {
51
180
  const s = this.ic.s;
52
181
  const stats = s.stats;
182
+ const stateColor = s.state === "streaming"
183
+ ? "#9ece6a"
184
+ : s.state === "connecting"
185
+ ? "#7aa2f7"
186
+ : s.state === "error"
187
+ ? "#f7768e"
188
+ : "#c0caf5";
53
189
  return html `
54
190
  <div class="section">
55
- <div class="label">Connection</div>
56
- ${this._row("State", s.state)}
57
- ${this._row("WebCodecs", s.isWebCodecsActive ? "Active" : s.useWebCodecs ? "Pending" : "Off")}
191
+ <div class="section-header" style="margin-bottom:4px">Connection</div>
192
+ <div style="font-size:14px;font-weight:600;color:${stateColor}">
193
+ ${s.state.charAt(0).toUpperCase() + s.state.slice(1)}
194
+ </div>
58
195
  </div>
59
196
  ${stats
60
197
  ? html `
61
- <div class="section">
62
- <div class="label">WebRTC Stats</div>
63
- ${this._row("Video bitrate", stats.video.bitrate ? `${Math.round(stats.video.bitrate / 1000)} kbps` : "—")}
64
- ${this._row("Audio bitrate", stats.audio.bitrate ? `${Math.round(stats.audio.bitrate / 1000)} kbps` : "—")}
65
- ${this._row("RTT", stats.connection.rtt ? `${(stats.connection.rtt * 1000).toFixed(0)} ms` : "—")}
66
- ${this._row("FPS", stats.video.framesPerSecond ? String(stats.video.framesPerSecond) : "—")}
67
- ${this._row("Packets sent", String(stats.video.packetsSent))}
68
- ${this._row("Packets lost", String(stats.video.packetsLost))}
198
+ <div class="row">
199
+ <span class="row-label">Bitrate</span>
200
+ <span class="row-value">
201
+ ${formatBitrate(stats.video.bitrate + stats.audio.bitrate)}
202
+ </span>
203
+ </div>
204
+ <div class="row">
205
+ <span class="row-label">Video</span>
206
+ <span class="row-value" style="color:#7aa2f7">
207
+ ${formatBitrate(stats.video.bitrate)}
208
+ </span>
209
+ </div>
210
+ <div class="row">
211
+ <span class="row-label">Audio</span>
212
+ <span class="row-value" style="color:#7aa2f7">
213
+ ${formatBitrate(stats.audio.bitrate)}
214
+ </span>
215
+ </div>
216
+ <div class="row">
217
+ <span class="row-label">Frame Rate</span>
218
+ <span class="row-value">
219
+ ${stats.video.framesPerSecond.toFixed(0)} fps
220
+ </span>
221
+ </div>
222
+ <div class="row">
223
+ <span class="row-label">Frames Encoded</span>
224
+ <span class="row-value">${stats.video.framesEncoded}</span>
225
+ </div>
226
+ ${stats.video.packetsLost > 0 || stats.audio.packetsLost > 0
227
+ ? html `
228
+ <div class="row">
229
+ <span class="row-label">Packets Lost</span>
230
+ <span class="row-value" style="color:#f7768e">
231
+ ${stats.video.packetsLost + stats.audio.packetsLost}
232
+ </span>
233
+ </div>
234
+ `
235
+ : nothing}
236
+ <div class="row">
237
+ <span class="row-label">RTT</span>
238
+ <span
239
+ class="row-value"
240
+ style="color:${stats.connection.rtt > 200 ? "#e0af68" : "#c0caf5"}"
241
+ >
242
+ ${stats.connection.rtt.toFixed(0)} ms
243
+ </span>
244
+ </div>
245
+ <div class="row">
246
+ <span class="row-label">ICE State</span>
247
+ <span class="row-value" style="text-transform:capitalize">
248
+ ${stats.connection.iceState}
249
+ </span>
250
+ </div>
251
+ `
252
+ : html `
253
+ <div style="color:#565f89;text-align:center;padding:24px">
254
+ ${s.state === "streaming"
255
+ ? "Waiting for stats..."
256
+ : "Start streaming to see stats"}
257
+ </div>
258
+ `}
259
+ ${s.error
260
+ ? html `
261
+ <div
262
+ style="padding:12px;border-top:1px solid rgba(247,118,142,0.3);background:rgba(247,118,142,0.1)"
263
+ >
264
+ <div class="section-header" style="color:#f7768e;margin-bottom:4px">
265
+ Error
266
+ </div>
267
+ <div style="font-size:12px;color:#f7768e">${s.error}</div>
69
268
  </div>
70
269
  `
71
270
  : nothing}
72
271
  ${s.encoderStats
73
272
  ? html `
74
- <div class="section">
75
- <div class="label">Encoder</div>
76
- ${this._row("Video frames", String(s.encoderStats.video.framesEncoded))}
77
- ${this._row("Video pending", String(s.encoderStats.video.framesPending))}
78
- ${this._row("Audio samples", String(s.encoderStats.audio.samplesEncoded))}
273
+ <div style="border-top:1px solid rgba(65,72,104,0.3)">
274
+ <div class="section-dark">
275
+ <span class="section-header" style="margin-bottom:0">Encoder</span>
276
+ </div>
277
+ <div class="row">
278
+ <span class="row-label">Video frames</span>
279
+ <span class="row-value">${s.encoderStats.video.framesEncoded}</span>
280
+ </div>
281
+ <div class="row">
282
+ <span class="row-label">Video pending</span>
283
+ <span
284
+ class="row-value"
285
+ style="color:${s.encoderStats.video.framesPending > 5
286
+ ? "#e0af68"
287
+ : "#c0caf5"}"
288
+ >
289
+ ${s.encoderStats.video.framesPending}
290
+ </span>
291
+ </div>
292
+ <div class="row">
293
+ <span class="row-label">Audio samples</span>
294
+ <span class="row-value">${s.encoderStats.audio.samplesEncoded}</span>
295
+ </div>
79
296
  </div>
80
297
  `
81
298
  : nothing}
82
299
  `;
83
300
  }
301
+ // ---- Info Tab ----
84
302
  _renderInfo() {
85
303
  const s = this.ic.s;
86
304
  return html `
87
305
  <div class="section">
88
- <div class="label">Configuration</div>
89
- ${this._row("Profile", s.qualityProfile)} ${this._row("Sources", String(s.sources.length))}
90
- ${this._row("WebCodecs Available", s.isWebCodecsAvailable ? "Yes" : "No")}
306
+ <div class="section-header" style="margin-bottom:4px">Quality Profile</div>
307
+ <div style="font-size:14px;color:#c0caf5;text-transform:capitalize">
308
+ ${s.qualityProfile}
309
+ </div>
91
310
  </div>
311
+
92
312
  <div class="section">
93
- <div class="label">Sources</div>
94
- ${s.sources.map((source) => html `
95
- ${this._row(source.label, `${source.type} ${source.muted ? "(muted)" : ""}`)}
96
- `)}
313
+ <div class="section-header" style="margin-bottom:4px">Configuration</div>
314
+ ${this._simpleRow("WebCodecs", s.isWebCodecsAvailable ? "Available" : "Unavailable")}
315
+ ${this._simpleRow("WebCodecs Active", s.isWebCodecsActive ? "Yes" : s.useWebCodecs ? "Pending" : "No")}
316
+ ${this._simpleRow("Sources", String(s.sources.length))}
317
+ </div>
318
+
319
+ ${s.sources.length > 0
320
+ ? html `
321
+ <div style="border-bottom:1px solid rgba(65,72,104,0.3)">
322
+ <div class="section-dark">
323
+ <span class="section-header" style="margin-bottom:0">
324
+ Sources (${s.sources.length})
325
+ </span>
326
+ </div>
327
+ ${s.sources.map((source, idx) => html `
328
+ <div
329
+ style="padding:8px 12px;${idx > 0
330
+ ? "border-top:1px solid rgba(65,72,104,0.2)"
331
+ : ""}"
332
+ >
333
+ <div style="display:flex;align-items:center;gap:8px">
334
+ <span
335
+ class="source-type"
336
+ style="background:${source.type === "camera"
337
+ ? "rgba(122,162,247,0.2)"
338
+ : source.type === "screen"
339
+ ? "rgba(158,206,106,0.2)"
340
+ : "rgba(224,175,104,0.2)"};color:${source.type === "camera"
341
+ ? "#7aa2f7"
342
+ : source.type === "screen"
343
+ ? "#9ece6a"
344
+ : "#e0af68"}"
345
+ >
346
+ ${source.type}
347
+ </span>
348
+ <span
349
+ style="color:#c0caf5;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"
350
+ >
351
+ ${source.label}
352
+ </span>
353
+ </div>
354
+ <div
355
+ style="display:flex;gap:12px;margin-top:4px;font-size:10px;color:#565f89"
356
+ >
357
+ <span>Vol: ${Math.round(source.volume * 100)}%</span>
358
+ ${source.muted
359
+ ? html `<span style="color:#f7768e">Muted</span>`
360
+ : nothing}
361
+ </div>
362
+ </div>
363
+ `)}
364
+ </div>
365
+ `
366
+ : nothing}
367
+ `;
368
+ }
369
+ // ---- Compositor Tab ----
370
+ _renderCompositor() {
371
+ const s = this.ic.s;
372
+ const rt = this.compositorRendererType;
373
+ const stats = this.compositorStats;
374
+ const rendererColor = rt === "webgpu" ? "#bb9af7" : rt === "webgl" ? "#7aa2f7" : "#9ece6a";
375
+ const rendererLabel = rt === "webgpu"
376
+ ? "WebGPU"
377
+ : rt === "webgl"
378
+ ? "WebGL"
379
+ : rt === "canvas2d"
380
+ ? "Canvas2D"
381
+ : "Not initialized";
382
+ return html `
383
+ <div class="section">
384
+ <div class="section-header">Renderer</div>
385
+ <div style="font-size:14px;font-weight:600;color:${rendererColor}">
386
+ ${rendererLabel}
387
+ </div>
388
+ </div>
389
+
390
+ ${stats
391
+ ? html `
392
+ <div style="border-bottom:1px solid rgba(65,72,104,0.3)">
393
+ <div class="section-dark">
394
+ <span class="section-header" style="margin-bottom:0">Performance</span>
395
+ </div>
396
+ <div class="row">
397
+ <span class="row-label">Frame Rate</span>
398
+ <span class="row-value">${stats.fps} fps</span>
399
+ </div>
400
+ <div class="row">
401
+ <span class="row-label">Frame Time</span>
402
+ <span
403
+ class="row-value"
404
+ style="color:${stats.frameTimeMs > 16 ? "#e0af68" : "#c0caf5"}"
405
+ >
406
+ ${stats.frameTimeMs.toFixed(2)} ms
407
+ </span>
408
+ </div>
409
+ ${stats.gpuMemoryMB !== undefined
410
+ ? html `
411
+ <div class="row">
412
+ <span class="row-label">GPU Memory</span>
413
+ <span class="row-value">
414
+ ${stats.gpuMemoryMB.toFixed(1)} MB
415
+ </span>
416
+ </div>
417
+ `
418
+ : nothing}
419
+ </div>
420
+ `
421
+ : nothing}
422
+
423
+ <div style="border-bottom:1px solid rgba(65,72,104,0.3)">
424
+ <div class="section-dark">
425
+ <span class="section-header" style="margin-bottom:0">Composition</span>
426
+ </div>
427
+ <div class="row">
428
+ <span class="row-label">Scenes</span>
429
+ <span class="row-value">${this.sceneCount}</span>
430
+ </div>
431
+ <div class="row">
432
+ <span class="row-label">Layers</span>
433
+ <span class="row-value">${this.layerCount}</span>
434
+ </div>
435
+ </div>
436
+
437
+ <!-- Encoder -->
438
+ <div style="border-bottom:1px solid rgba(65,72,104,0.3)">
439
+ <div class="section-dark">
440
+ <span class="section-header" style="margin-bottom:0">Encoder</span>
441
+ </div>
442
+ <div class="row">
443
+ <span class="row-label">Type</span>
444
+ <span
445
+ class="badge"
446
+ style="background:${s.useWebCodecs && s.isWebCodecsAvailable
447
+ ? "rgba(187,154,247,0.2)"
448
+ : "rgba(122,162,247,0.2)"};color:${s.useWebCodecs && s.isWebCodecsAvailable
449
+ ? "#bb9af7"
450
+ : "#7aa2f7"}"
451
+ >
452
+ ${s.useWebCodecs && s.isWebCodecsAvailable ? "WebCodecs" : "Browser"}
453
+ ${s.state === "streaming"
454
+ ? html `<span style="opacity:0.7;margin-left:4px">
455
+ ${s.isWebCodecsActive ? "(active)" : "(pending)"}
456
+ </span>`
457
+ : nothing}
458
+ </span>
459
+ </div>
460
+ <div class="processing-row">
461
+ <span class="processing-label">Use WebCodecs</span>
462
+ <button
463
+ type="button"
464
+ role="switch"
465
+ aria-checked=${s.useWebCodecs}
466
+ class="toggle ${s.useWebCodecs ? "toggle--on" : "toggle--off"}"
467
+ ?disabled=${s.state === "streaming" || !s.isWebCodecsAvailable}
468
+ @click=${() => this.ic.setUseWebCodecs(!s.useWebCodecs)}
469
+ >
470
+ <div class="toggle-knob"></div>
471
+ </button>
472
+ </div>
473
+ ${!s.isWebCodecsAvailable
474
+ ? html `<div style="padding:8px 12px;font-size:10px;color:#f7768e">
475
+ Not available - RTCRtpScriptTransform unsupported
476
+ </div>`
477
+ : nothing}
97
478
  </div>
98
479
  `;
99
480
  }
100
- _row(label, value) {
481
+ _simpleRow(label, value) {
101
482
  return html `<div class="row">
102
483
  <span class="row-label">${label}</span><span class="row-value">${value}</span>
103
484
  </div>`;
@@ -111,68 +492,80 @@ FwScAdvanced.styles = [
111
492
  display: block;
112
493
  }
113
494
  .panel {
114
- width: 320px;
495
+ width: 280px;
115
496
  height: 100%;
116
- border-left: 1px solid rgba(90, 96, 127, 0.3);
497
+ border-left: 1px solid rgba(65, 72, 104, 0.5);
117
498
  background: #1a1b26;
118
- overflow: auto;
119
- font-size: 0.75rem;
499
+ display: flex;
500
+ flex-direction: column;
501
+ font-size: 12px;
502
+ font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, monospace;
120
503
  color: #a9b1d6;
504
+ flex-shrink: 0;
505
+ z-index: 40;
121
506
  }
122
507
  .header {
123
508
  display: flex;
124
509
  align-items: center;
125
- justify-content: space-between;
126
- padding: 0.5rem 0.75rem;
127
- border-bottom: 1px solid rgba(90, 96, 127, 0.3);
128
- }
129
- .tabs {
130
- display: flex;
131
- gap: 0.5rem;
510
+ border-bottom: 1px solid rgba(65, 72, 104, 0.3);
511
+ background: #16161e;
132
512
  }
133
513
  .tab {
134
- padding: 0.25rem 0.5rem;
514
+ padding: 8px 12px;
515
+ font-size: 10px;
516
+ text-transform: uppercase;
517
+ letter-spacing: 0.05em;
518
+ font-weight: 600;
135
519
  border: none;
136
- background: none;
520
+ background: transparent;
137
521
  color: #565f89;
138
- font-size: 0.6875rem;
139
- font-weight: 600;
140
522
  cursor: pointer;
141
- border-radius: 0.25rem;
523
+ transition: all 0.15s;
142
524
  }
143
525
  .tab--active {
526
+ background: #1a1b26;
144
527
  color: #c0caf5;
145
- background: rgba(90, 96, 127, 0.2);
146
528
  }
147
529
  .close {
148
530
  display: flex;
149
- background: none;
531
+ background: transparent;
150
532
  border: none;
151
533
  color: #565f89;
152
534
  cursor: pointer;
153
- padding: 0;
535
+ padding: 8px;
536
+ transition: color 0.15s;
154
537
  }
155
538
  .close:hover {
156
539
  color: #c0caf5;
157
540
  }
158
541
  .body {
159
- padding: 0.75rem;
160
- }
161
- .section {
162
- margin-bottom: 0.75rem;
542
+ flex: 1;
543
+ overflow-y: auto;
163
544
  }
164
- .label {
165
- font-size: 0.625rem;
166
- font-weight: 600;
545
+ .section-header {
546
+ font-size: 10px;
547
+ color: #565f89;
167
548
  text-transform: uppercase;
168
549
  letter-spacing: 0.05em;
169
- color: #565f89;
170
- margin-bottom: 0.375rem;
550
+ font-weight: 600;
551
+ margin-bottom: 8px;
552
+ }
553
+ .section {
554
+ padding: 12px;
555
+ border-bottom: 1px solid rgba(65, 72, 104, 0.3);
556
+ }
557
+ .section-dark {
558
+ padding: 8px 12px;
559
+ background: #16161e;
560
+ display: flex;
561
+ justify-content: space-between;
562
+ align-items: center;
171
563
  }
172
564
  .row {
173
565
  display: flex;
174
566
  justify-content: space-between;
175
- padding: 0.125rem 0;
567
+ padding: 8px 12px;
568
+ border-top: 1px solid rgba(65, 72, 104, 0.2);
176
569
  }
177
570
  .row-label {
178
571
  color: #565f89;
@@ -182,11 +575,102 @@ FwScAdvanced.styles = [
182
575
  font-family: ui-monospace, monospace;
183
576
  font-variant-numeric: tabular-nums;
184
577
  }
578
+ .level-bar {
579
+ height: 8px;
580
+ background: rgba(65, 72, 104, 0.3);
581
+ border-radius: 4px;
582
+ overflow: hidden;
583
+ }
584
+ .level-fill {
585
+ height: 100%;
586
+ transition: all 75ms;
587
+ }
588
+ .level-labels {
589
+ display: flex;
590
+ justify-content: space-between;
591
+ font-size: 10px;
592
+ color: #565f89;
593
+ margin-top: 4px;
594
+ }
595
+ .badge {
596
+ font-size: 12px;
597
+ font-family: monospace;
598
+ padding: 2px 6px;
599
+ }
600
+ .toggle {
601
+ position: relative;
602
+ display: inline-flex;
603
+ height: 20px;
604
+ width: 36px;
605
+ flex-shrink: 0;
606
+ cursor: pointer;
607
+ border-radius: 10px;
608
+ border: 2px solid transparent;
609
+ transition: background 0.2s;
610
+ padding: 0;
611
+ }
612
+ .toggle:disabled {
613
+ opacity: 0.5;
614
+ cursor: not-allowed;
615
+ }
616
+ .toggle-knob {
617
+ position: absolute;
618
+ top: 2px;
619
+ width: 12px;
620
+ height: 12px;
621
+ border-radius: 50%;
622
+ background: white;
623
+ transition: left 0.2s;
624
+ }
625
+ .toggle--on {
626
+ background: #7aa2f7;
627
+ }
628
+ .toggle--off {
629
+ background: rgba(65, 72, 104, 0.5);
630
+ }
631
+ .toggle--on .toggle-knob {
632
+ left: 18px;
633
+ }
634
+ .toggle--off .toggle-knob {
635
+ left: 4px;
636
+ }
637
+ .processing-row {
638
+ display: flex;
639
+ justify-content: space-between;
640
+ align-items: center;
641
+ padding: 8px 12px;
642
+ border-top: 1px solid rgba(65, 72, 104, 0.2);
643
+ }
644
+ .processing-label {
645
+ font-size: 12px;
646
+ color: #a9b1d6;
647
+ }
648
+ .source-type {
649
+ font-size: 10px;
650
+ font-family: monospace;
651
+ padding: 2px 6px;
652
+ text-transform: uppercase;
653
+ }
185
654
  `,
186
655
  ];
187
656
  __decorate([
188
657
  property({ attribute: false })
189
658
  ], FwScAdvanced.prototype, "ic", void 0);
659
+ __decorate([
660
+ property({ type: Boolean, attribute: "compositor-enabled" })
661
+ ], FwScAdvanced.prototype, "compositorEnabled", void 0);
662
+ __decorate([
663
+ property({ type: String, attribute: "compositor-renderer" })
664
+ ], FwScAdvanced.prototype, "compositorRendererType", void 0);
665
+ __decorate([
666
+ property({ attribute: false })
667
+ ], FwScAdvanced.prototype, "compositorStats", void 0);
668
+ __decorate([
669
+ property({ type: Number, attribute: "scene-count" })
670
+ ], FwScAdvanced.prototype, "sceneCount", void 0);
671
+ __decorate([
672
+ property({ type: Number, attribute: "layer-count" })
673
+ ], FwScAdvanced.prototype, "layerCount", void 0);
190
674
  __decorate([
191
675
  state()
192
676
  ], FwScAdvanced.prototype, "_activeTab", void 0);