@livepeer-frameworks/streamcrafter-wc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/cjs/components/fw-sc-advanced.js +198 -0
  2. package/dist/cjs/components/fw-sc-advanced.js.map +1 -0
  3. package/dist/cjs/components/fw-sc-compositor.js +116 -0
  4. package/dist/cjs/components/fw-sc-compositor.js.map +1 -0
  5. package/dist/cjs/components/fw-sc-layer-list.js +253 -0
  6. package/dist/cjs/components/fw-sc-layer-list.js.map +1 -0
  7. package/dist/cjs/components/fw-sc-scene-switcher.js +164 -0
  8. package/dist/cjs/components/fw-sc-scene-switcher.js.map +1 -0
  9. package/dist/cjs/components/fw-sc-volume.js +183 -0
  10. package/dist/cjs/components/fw-sc-volume.js.map +1 -0
  11. package/dist/cjs/components/fw-streamcrafter.js +508 -0
  12. package/dist/cjs/components/fw-streamcrafter.js.map +1 -0
  13. package/dist/cjs/controllers/ingest-controller-host.js +236 -0
  14. package/dist/cjs/controllers/ingest-controller-host.js.map +1 -0
  15. package/dist/cjs/define.js +25 -0
  16. package/dist/cjs/define.js.map +1 -0
  17. package/dist/cjs/icons/index.js +283 -0
  18. package/dist/cjs/icons/index.js.map +1 -0
  19. package/dist/cjs/index.js +38 -0
  20. package/dist/cjs/index.js.map +1 -0
  21. package/dist/cjs/node_modules/.pnpm/@rollup_plugin-typescript@12.3.0_rollup@4.57.1_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js +33 -0
  22. package/dist/cjs/node_modules/.pnpm/@rollup_plugin-typescript@12.3.0_rollup@4.57.1_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js.map +1 -0
  23. package/dist/cjs/styles/shared-styles.js +2019 -0
  24. package/dist/cjs/styles/shared-styles.js.map +1 -0
  25. package/dist/cjs/styles/utility-styles.js +182 -0
  26. package/dist/cjs/styles/utility-styles.js.map +1 -0
  27. package/dist/esm/components/fw-sc-advanced.js +198 -0
  28. package/dist/esm/components/fw-sc-advanced.js.map +1 -0
  29. package/dist/esm/components/fw-sc-compositor.js +116 -0
  30. package/dist/esm/components/fw-sc-compositor.js.map +1 -0
  31. package/dist/esm/components/fw-sc-layer-list.js +253 -0
  32. package/dist/esm/components/fw-sc-layer-list.js.map +1 -0
  33. package/dist/esm/components/fw-sc-scene-switcher.js +164 -0
  34. package/dist/esm/components/fw-sc-scene-switcher.js.map +1 -0
  35. package/dist/esm/components/fw-sc-volume.js +183 -0
  36. package/dist/esm/components/fw-sc-volume.js.map +1 -0
  37. package/dist/esm/components/fw-streamcrafter.js +508 -0
  38. package/dist/esm/components/fw-streamcrafter.js.map +1 -0
  39. package/dist/esm/controllers/ingest-controller-host.js +234 -0
  40. package/dist/esm/controllers/ingest-controller-host.js.map +1 -0
  41. package/dist/esm/define.js +23 -0
  42. package/dist/esm/define.js.map +1 -0
  43. package/dist/esm/icons/index.js +253 -0
  44. package/dist/esm/icons/index.js.map +1 -0
  45. package/dist/esm/index.js +8 -0
  46. package/dist/esm/index.js.map +1 -0
  47. package/dist/esm/node_modules/.pnpm/@rollup_plugin-typescript@12.3.0_rollup@4.57.1_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js +31 -0
  48. package/dist/esm/node_modules/.pnpm/@rollup_plugin-typescript@12.3.0_rollup@4.57.1_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js.map +1 -0
  49. package/dist/esm/styles/shared-styles.js +2017 -0
  50. package/dist/esm/styles/shared-styles.js.map +1 -0
  51. package/dist/esm/styles/utility-styles.js +180 -0
  52. package/dist/esm/styles/utility-styles.js.map +1 -0
  53. package/dist/fw-streamcrafter.iife.js +3121 -0
  54. package/dist/fw-streamcrafter.iife.js.map +1 -0
  55. package/dist/types/components/fw-sc-advanced.d.ts +20 -0
  56. package/dist/types/components/fw-sc-compositor.d.ts +19 -0
  57. package/dist/types/components/fw-sc-layer-list.d.ts +30 -0
  58. package/dist/types/components/fw-sc-scene-switcher.d.ts +23 -0
  59. package/dist/types/components/fw-sc-volume.d.ts +30 -0
  60. package/dist/types/components/fw-streamcrafter.d.ts +49 -0
  61. package/dist/types/controllers/ingest-controller-host.d.ts +77 -0
  62. package/dist/types/define.d.ts +1 -0
  63. package/dist/types/icons/index.d.ts +29 -0
  64. package/dist/types/iife-entry.d.ts +11 -0
  65. package/dist/types/index.d.ts +12 -0
  66. package/dist/types/styles/shared-styles.d.ts +1 -0
  67. package/dist/types/styles/utility-styles.d.ts +1 -0
  68. package/package.json +55 -0
  69. package/src/components/fw-sc-advanced.ts +221 -0
  70. package/src/components/fw-sc-compositor.ts +162 -0
  71. package/src/components/fw-sc-layer-list.ts +251 -0
  72. package/src/components/fw-sc-scene-switcher.ts +163 -0
  73. package/src/components/fw-sc-volume.ts +171 -0
  74. package/src/components/fw-streamcrafter.ts +515 -0
  75. package/src/controllers/ingest-controller-host.ts +358 -0
  76. package/src/define.ts +23 -0
  77. package/src/icons/index.ts +291 -0
  78. package/src/iife-entry.ts +11 -0
  79. package/src/index.ts +15 -0
  80. package/src/styles/shared-styles.ts +2014 -0
  81. package/src/styles/utility-styles.ts +177 -0
@@ -0,0 +1,358 @@
1
+ /**
2
+ * IngestControllerHost — Lit ReactiveController wrapping IngestControllerV2.
3
+ * Direct port of useStreamCrafterV2.ts from streamcrafter-react.
4
+ */
5
+ import type { ReactiveController, ReactiveControllerHost } from "lit";
6
+ import {
7
+ IngestControllerV2,
8
+ type IngestControllerConfigV2,
9
+ type IngestState,
10
+ type IngestStateContextV2,
11
+ type IngestStats,
12
+ type CaptureOptions,
13
+ type ScreenCaptureOptions,
14
+ type DeviceInfo,
15
+ type MediaSource,
16
+ type QualityProfile,
17
+ type ReconnectionState,
18
+ type EncoderOverrides,
19
+ detectCapabilities,
20
+ isWebCodecsEncodingPathSupported,
21
+ } from "@livepeer-frameworks/streamcrafter-core";
22
+
23
+ export interface EncoderStats {
24
+ video: {
25
+ framesEncoded: number;
26
+ framesPending: number;
27
+ bytesEncoded: number;
28
+ lastFrameTime: number;
29
+ };
30
+ audio: {
31
+ samplesEncoded: number;
32
+ samplesPending: number;
33
+ bytesEncoded: number;
34
+ lastSampleTime: number;
35
+ };
36
+ timestamp: number;
37
+ }
38
+
39
+ export interface IngestControllerHostState {
40
+ state: IngestState;
41
+ stateContext: IngestStateContextV2;
42
+ isStreaming: boolean;
43
+ isCapturing: boolean;
44
+ isReconnecting: boolean;
45
+ error: string | null;
46
+ mediaStream: MediaStream | null;
47
+ sources: MediaSource[];
48
+ qualityProfile: QualityProfile;
49
+ reconnectionState: ReconnectionState | null;
50
+ stats: IngestStats | null;
51
+ useWebCodecs: boolean;
52
+ isWebCodecsActive: boolean;
53
+ isWebCodecsAvailable: boolean;
54
+ encoderStats: EncoderStats | null;
55
+ }
56
+
57
+ type HostElement = ReactiveControllerHost & HTMLElement;
58
+
59
+ export class IngestControllerHost implements ReactiveController {
60
+ host: HostElement;
61
+ private controller: IngestControllerV2 | null = null;
62
+ private unsubs: Array<() => void> = [];
63
+ private encoderStatsCleanup: (() => void) | null = null;
64
+
65
+ s: IngestControllerHostState;
66
+
67
+ constructor(host: HostElement, initialProfile: QualityProfile = "broadcast") {
68
+ this.host = host;
69
+ host.addController(this);
70
+
71
+ const capabilities = detectCapabilities();
72
+ this.s = {
73
+ state: "idle",
74
+ stateContext: {},
75
+ isStreaming: false,
76
+ isCapturing: false,
77
+ isReconnecting: false,
78
+ error: null,
79
+ mediaStream: null,
80
+ sources: [],
81
+ qualityProfile: initialProfile,
82
+ reconnectionState: null,
83
+ stats: null,
84
+ useWebCodecs: capabilities.recommended === "webcodecs",
85
+ isWebCodecsActive: false,
86
+ isWebCodecsAvailable: isWebCodecsEncodingPathSupported(),
87
+ encoderStats: null,
88
+ };
89
+ }
90
+
91
+ // ---- Configuration & Lifecycle ----
92
+
93
+ initialize(config: IngestControllerConfigV2) {
94
+ this.teardown();
95
+
96
+ const controller = new IngestControllerV2({
97
+ ...config,
98
+ useWebCodecs: this.s.useWebCodecs,
99
+ });
100
+ this.controller = controller;
101
+ this.subscribeToEvents(controller);
102
+ }
103
+
104
+ hostConnected() {}
105
+
106
+ hostDisconnected() {
107
+ this.teardown();
108
+ }
109
+
110
+ private teardown() {
111
+ this.unsubs.forEach((fn) => fn());
112
+ this.unsubs = [];
113
+ if (this.encoderStatsCleanup) {
114
+ this.encoderStatsCleanup();
115
+ this.encoderStatsCleanup = null;
116
+ }
117
+ this.controller?.destroy();
118
+ this.controller = null;
119
+ }
120
+
121
+ // ---- State Updates ----
122
+
123
+ private update(partial: Partial<IngestControllerHostState>) {
124
+ Object.assign(this.s, partial);
125
+ this.host.requestUpdate();
126
+ }
127
+
128
+ // ---- Event Subscriptions ----
129
+
130
+ private subscribeToEvents(controller: IngestControllerV2) {
131
+ const u = this.unsubs;
132
+
133
+ u.push(
134
+ controller.on("stateChange", (event) => {
135
+ const state = event.state;
136
+ const ctx = (event.context ?? {}) as IngestStateContextV2;
137
+ this.update({
138
+ state,
139
+ stateContext: ctx,
140
+ isStreaming: state === "streaming",
141
+ isCapturing: state === "capturing" || state === "streaming",
142
+ isReconnecting: state === "reconnecting",
143
+ mediaStream: controller.getMediaStream(),
144
+ sources: controller.getSources(),
145
+ reconnectionState: ctx.reconnection ?? this.s.reconnectionState,
146
+ });
147
+ this.dispatchEvent("fw-sc-state-change", { state, context: ctx });
148
+ })
149
+ );
150
+
151
+ u.push(
152
+ controller.on("statsUpdate", (newStats) => {
153
+ this.update({ stats: newStats });
154
+ })
155
+ );
156
+
157
+ u.push(
158
+ controller.on("error", (event) => {
159
+ this.update({ error: event.error });
160
+ this.dispatchEvent("fw-sc-error", { error: event.error });
161
+ })
162
+ );
163
+
164
+ u.push(
165
+ controller.on("sourceAdded", () => {
166
+ this.update({
167
+ sources: controller.getSources(),
168
+ mediaStream: controller.getMediaStream(),
169
+ });
170
+ })
171
+ );
172
+
173
+ u.push(
174
+ controller.on("sourceRemoved", () => {
175
+ this.update({
176
+ sources: controller.getSources(),
177
+ mediaStream: controller.getMediaStream(),
178
+ });
179
+ })
180
+ );
181
+
182
+ u.push(
183
+ controller.on("sourceUpdated", () => {
184
+ this.update({
185
+ sources: controller.getSources(),
186
+ mediaStream: controller.getMediaStream(),
187
+ });
188
+ })
189
+ );
190
+
191
+ u.push(
192
+ controller.on("qualityChanged", (event) => {
193
+ this.update({ qualityProfile: event.profile });
194
+ })
195
+ );
196
+
197
+ u.push(
198
+ controller.on("reconnectionAttempt", () => {
199
+ this.update({
200
+ reconnectionState: controller.getReconnectionManager().getState(),
201
+ });
202
+ })
203
+ );
204
+
205
+ u.push(
206
+ controller.on("webCodecsActive", (event: { active: boolean }) => {
207
+ this.update({ isWebCodecsActive: event.active });
208
+ if (event.active) {
209
+ this.setupEncoderStatsListener();
210
+ }
211
+ })
212
+ );
213
+
214
+ // Monitor encoder status on state changes
215
+ u.push(
216
+ controller.on("stateChange", (event) => {
217
+ if (event.state === "streaming") {
218
+ setTimeout(() => {
219
+ this.update({ isWebCodecsActive: controller.isWebCodecsActive() });
220
+ if (controller.isWebCodecsActive() && !this.encoderStatsCleanup) {
221
+ this.setupEncoderStatsListener();
222
+ }
223
+ }, 200);
224
+ } else if (event.state === "idle" || event.state === "capturing") {
225
+ this.update({ isWebCodecsActive: false, encoderStats: null });
226
+ if (this.encoderStatsCleanup) {
227
+ this.encoderStatsCleanup();
228
+ this.encoderStatsCleanup = null;
229
+ }
230
+ }
231
+ })
232
+ );
233
+ }
234
+
235
+ private setupEncoderStatsListener() {
236
+ if (!this.controller) return;
237
+ const encoder = this.controller.getEncoderManager();
238
+ if (encoder) {
239
+ this.encoderStatsCleanup = encoder.on("stats", (newStats) => {
240
+ this.update({ encoderStats: newStats as EncoderStats });
241
+ });
242
+ }
243
+ }
244
+
245
+ private dispatchEvent(name: string, detail: unknown) {
246
+ this.host.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));
247
+ }
248
+
249
+ // ---- Capture Actions ----
250
+
251
+ async startCamera(options?: CaptureOptions): Promise<MediaSource> {
252
+ if (!this.controller) throw new Error("Controller not initialized");
253
+ this.update({ error: null });
254
+ return this.controller.startCamera(options);
255
+ }
256
+
257
+ async startScreenShare(options?: ScreenCaptureOptions): Promise<MediaSource | null> {
258
+ if (!this.controller) throw new Error("Controller not initialized");
259
+ this.update({ error: null });
260
+ return this.controller.startScreenShare(options);
261
+ }
262
+
263
+ addCustomSource(stream: MediaStream, label: string): MediaSource {
264
+ if (!this.controller) throw new Error("Controller not initialized");
265
+ return this.controller.addCustomSource(stream, label);
266
+ }
267
+
268
+ removeSource(sourceId: string) {
269
+ this.controller?.removeSource(sourceId);
270
+ }
271
+
272
+ async stopCapture() {
273
+ await this.controller?.stopCapture();
274
+ }
275
+
276
+ // ---- Source Management ----
277
+
278
+ setSourceVolume(sourceId: string, volume: number) {
279
+ this.controller?.setSourceVolume(sourceId, volume);
280
+ }
281
+
282
+ setSourceMuted(sourceId: string, muted: boolean) {
283
+ this.controller?.setSourceMuted(sourceId, muted);
284
+ }
285
+
286
+ setSourceActive(sourceId: string, active: boolean) {
287
+ this.controller?.setSourceActive(sourceId, active);
288
+ }
289
+
290
+ setPrimaryVideoSource(sourceId: string) {
291
+ this.controller?.setPrimaryVideoSource(sourceId);
292
+ }
293
+
294
+ // ---- Master Audio ----
295
+
296
+ setMasterVolume(volume: number) {
297
+ this.controller?.setMasterVolume(volume);
298
+ }
299
+
300
+ getMasterVolume(): number {
301
+ return this.controller?.getMasterVolume() ?? 1;
302
+ }
303
+
304
+ // ---- Quality ----
305
+
306
+ async setQualityProfile(profile: QualityProfile) {
307
+ await this.controller?.setQualityProfile(profile);
308
+ }
309
+
310
+ // ---- Streaming ----
311
+
312
+ async startStreaming() {
313
+ if (!this.controller) throw new Error("Controller not initialized");
314
+ this.update({ error: null });
315
+ await this.controller.startStreaming();
316
+ }
317
+
318
+ async stopStreaming() {
319
+ await this.controller?.stopStreaming();
320
+ }
321
+
322
+ // ---- Devices ----
323
+
324
+ async getDevices(): Promise<DeviceInfo[]> {
325
+ return this.controller?.getDevices() ?? [];
326
+ }
327
+
328
+ async switchVideoDevice(deviceId: string) {
329
+ await this.controller?.switchVideoDevice(deviceId);
330
+ }
331
+
332
+ async switchAudioDevice(deviceId: string) {
333
+ await this.controller?.switchAudioDevice(deviceId);
334
+ }
335
+
336
+ // ---- Stats ----
337
+
338
+ async getStats(): Promise<IngestStats | null> {
339
+ return this.controller?.getStats() ?? null;
340
+ }
341
+
342
+ // ---- Encoder ----
343
+
344
+ setUseWebCodecs(enabled: boolean) {
345
+ this.update({ useWebCodecs: enabled });
346
+ this.controller?.setUseWebCodecs(enabled);
347
+ }
348
+
349
+ setEncoderOverrides(overrides: EncoderOverrides) {
350
+ this.controller?.setEncoderOverrides(overrides);
351
+ }
352
+
353
+ // ---- Controller Access ----
354
+
355
+ getController(): IngestControllerV2 | null {
356
+ return this.controller;
357
+ }
358
+ }
package/src/define.ts ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Side-effect import that registers all custom elements.
3
+ * Usage: import '@livepeer-frameworks/streamcrafter-wc/define';
4
+ */
5
+ import { FwStreamCrafter } from "./components/fw-streamcrafter.js";
6
+ import { FwScCompositor } from "./components/fw-sc-compositor.js";
7
+ import { FwScSceneSwitcher } from "./components/fw-sc-scene-switcher.js";
8
+ import { FwScLayerList } from "./components/fw-sc-layer-list.js";
9
+ import { FwScVolume } from "./components/fw-sc-volume.js";
10
+ import { FwScAdvanced } from "./components/fw-sc-advanced.js";
11
+
12
+ function safeDefine(name: string, ctor: CustomElementConstructor) {
13
+ if (!customElements.get(name)) {
14
+ customElements.define(name, ctor);
15
+ }
16
+ }
17
+
18
+ safeDefine("fw-streamcrafter", FwStreamCrafter);
19
+ safeDefine("fw-sc-compositor", FwScCompositor);
20
+ safeDefine("fw-sc-scene-switcher", FwScSceneSwitcher);
21
+ safeDefine("fw-sc-layer-list", FwScLayerList);
22
+ safeDefine("fw-sc-volume", FwScVolume);
23
+ safeDefine("fw-sc-advanced", FwScAdvanced);
@@ -0,0 +1,291 @@
1
+ /**
2
+ * SVG icons as Lit html template functions for StreamCrafter WC.
3
+ * Ported from StreamCrafter.tsx and CompositorControls.tsx inline SVGs.
4
+ */
5
+ import { html } from "lit";
6
+
7
+ export const cameraIcon = (size = 18) =>
8
+ html` <svg
9
+ width="${size}"
10
+ height="${size}"
11
+ viewBox="0 0 24 24"
12
+ fill="none"
13
+ stroke="currentColor"
14
+ stroke-width="2"
15
+ stroke-linecap="round"
16
+ stroke-linejoin="round"
17
+ >
18
+ <path d="M23 7l-7 5 7 5V7z" />
19
+ <rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
20
+ </svg>`;
21
+
22
+ export const monitorIcon = (size = 18) =>
23
+ html` <svg
24
+ width="${size}"
25
+ height="${size}"
26
+ viewBox="0 0 24 24"
27
+ fill="none"
28
+ stroke="currentColor"
29
+ stroke-width="2"
30
+ stroke-linecap="round"
31
+ stroke-linejoin="round"
32
+ >
33
+ <rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
34
+ <line x1="8" y1="21" x2="16" y2="21" />
35
+ <line x1="12" y1="17" x2="12" y2="21" />
36
+ </svg>`;
37
+
38
+ export const micIcon = (size = 16) =>
39
+ html` <svg
40
+ width="${size}"
41
+ height="${size}"
42
+ viewBox="0 0 24 24"
43
+ fill="none"
44
+ stroke="currentColor"
45
+ stroke-width="2"
46
+ stroke-linecap="round"
47
+ stroke-linejoin="round"
48
+ >
49
+ <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
50
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
51
+ <line x1="12" y1="19" x2="12" y2="23" />
52
+ <line x1="8" y1="23" x2="16" y2="23" />
53
+ </svg>`;
54
+
55
+ export const micMutedIcon = (size = 16) =>
56
+ html` <svg
57
+ width="${size}"
58
+ height="${size}"
59
+ viewBox="0 0 24 24"
60
+ fill="none"
61
+ stroke="currentColor"
62
+ stroke-width="2"
63
+ stroke-linecap="round"
64
+ stroke-linejoin="round"
65
+ >
66
+ <line x1="1" y1="1" x2="23" y2="23" />
67
+ <path d="M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6" />
68
+ <path d="M17 16.95A7 7 0 0 1 5 12v-2m14 0v2a7 7 0 0 1-.11 1.23" />
69
+ <line x1="12" y1="19" x2="12" y2="23" />
70
+ <line x1="8" y1="23" x2="16" y2="23" />
71
+ </svg>`;
72
+
73
+ export const xIcon = (size = 14) =>
74
+ html` <svg
75
+ width="${size}"
76
+ height="${size}"
77
+ viewBox="0 0 24 24"
78
+ fill="none"
79
+ stroke="currentColor"
80
+ stroke-width="2"
81
+ stroke-linecap="round"
82
+ stroke-linejoin="round"
83
+ >
84
+ <line x1="18" y1="6" x2="6" y2="18" />
85
+ <line x1="6" y1="6" x2="18" y2="18" />
86
+ </svg>`;
87
+
88
+ export const settingsIcon = (size = 16) =>
89
+ html` <svg
90
+ width="${size}"
91
+ height="${size}"
92
+ viewBox="0 0 24 24"
93
+ fill="none"
94
+ stroke="currentColor"
95
+ stroke-width="2"
96
+ stroke-linecap="round"
97
+ stroke-linejoin="round"
98
+ >
99
+ <circle cx="12" cy="12" r="3" />
100
+ <path
101
+ d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"
102
+ />
103
+ </svg>`;
104
+
105
+ export const chevronsRightIcon = (size = 14) =>
106
+ html` <svg
107
+ width="${size}"
108
+ height="${size}"
109
+ viewBox="0 0 24 24"
110
+ fill="none"
111
+ stroke="currentColor"
112
+ stroke-width="2"
113
+ stroke-linecap="round"
114
+ stroke-linejoin="round"
115
+ >
116
+ <polyline points="13 17 18 12 13 7" />
117
+ <polyline points="6 17 11 12 6 7" />
118
+ </svg>`;
119
+
120
+ export const chevronsLeftIcon = (size = 14) =>
121
+ html` <svg
122
+ width="${size}"
123
+ height="${size}"
124
+ viewBox="0 0 24 24"
125
+ fill="none"
126
+ stroke="currentColor"
127
+ stroke-width="2"
128
+ stroke-linecap="round"
129
+ stroke-linejoin="round"
130
+ >
131
+ <polyline points="11 17 6 12 11 7" />
132
+ <polyline points="18 17 13 12 18 7" />
133
+ </svg>`;
134
+
135
+ export const eyeIcon = (size = 14) =>
136
+ html` <svg
137
+ width="${size}"
138
+ height="${size}"
139
+ viewBox="0 0 24 24"
140
+ fill="none"
141
+ stroke="currentColor"
142
+ stroke-width="2"
143
+ stroke-linecap="round"
144
+ stroke-linejoin="round"
145
+ >
146
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
147
+ <circle cx="12" cy="12" r="3" />
148
+ </svg>`;
149
+
150
+ export const eyeOffIcon = (size = 14) =>
151
+ html` <svg
152
+ width="${size}"
153
+ height="${size}"
154
+ viewBox="0 0 24 24"
155
+ fill="none"
156
+ stroke="currentColor"
157
+ stroke-width="2"
158
+ stroke-linecap="round"
159
+ stroke-linejoin="round"
160
+ >
161
+ <path
162
+ d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"
163
+ />
164
+ <line x1="1" y1="1" x2="23" y2="23" />
165
+ </svg>`;
166
+
167
+ export const videoIcon = (size = 14) =>
168
+ html` <svg
169
+ width="${size}"
170
+ height="${size}"
171
+ viewBox="0 0 24 24"
172
+ fill="none"
173
+ stroke="currentColor"
174
+ stroke-width="2"
175
+ stroke-linecap="round"
176
+ stroke-linejoin="round"
177
+ >
178
+ <polygon points="23 7 16 12 23 17 23 7" />
179
+ <rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
180
+ </svg>`;
181
+
182
+ // Layout icons (12x12 compositor presets)
183
+ export const soloIcon = () =>
184
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
185
+ <rect x="1" y="1" width="10" height="10" rx="1" />
186
+ </svg>`;
187
+ export const pipBRIcon = () =>
188
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
189
+ <rect x="1" y="1" width="10" height="10" rx="1" fill-opacity="0.3" />
190
+ <rect x="6.5" y="6.5" width="4" height="3" rx="0.5" />
191
+ </svg>`;
192
+ export const pipBLIcon = () =>
193
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
194
+ <rect x="1" y="1" width="10" height="10" rx="1" fill-opacity="0.3" />
195
+ <rect x="1.5" y="6.5" width="4" height="3" rx="0.5" />
196
+ </svg>`;
197
+ export const pipTRIcon = () =>
198
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
199
+ <rect x="1" y="1" width="10" height="10" rx="1" fill-opacity="0.3" />
200
+ <rect x="6.5" y="2.5" width="4" height="3" rx="0.5" />
201
+ </svg>`;
202
+ export const pipTLIcon = () =>
203
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
204
+ <rect x="1" y="1" width="10" height="10" rx="1" fill-opacity="0.3" />
205
+ <rect x="1.5" y="2.5" width="4" height="3" rx="0.5" />
206
+ </svg>`;
207
+ export const splitHIcon = () =>
208
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
209
+ <rect x="1" y="1" width="4.5" height="10" rx="1" />
210
+ <rect x="6.5" y="1" width="4.5" height="10" rx="1" />
211
+ </svg>`;
212
+ export const splitVIcon = () =>
213
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
214
+ <rect x="1" y="1" width="10" height="4.5" rx="1" />
215
+ <rect x="1" y="6.5" width="10" height="4.5" rx="1" />
216
+ </svg>`;
217
+ export const focusLIcon = () =>
218
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
219
+ <rect x="1" y="1" width="7" height="10" rx="1" />
220
+ <rect x="8.5" y="1" width="2.5" height="10" rx="1" fill-opacity="0.5" />
221
+ </svg>`;
222
+ export const focusRIcon = () =>
223
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
224
+ <rect x="1" y="1" width="2.5" height="10" rx="1" fill-opacity="0.5" />
225
+ <rect x="4" y="1" width="7" height="10" rx="1" />
226
+ </svg>`;
227
+ export const gridIcon = () =>
228
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
229
+ <rect x="1" y="1" width="4.5" height="4.5" rx="1" />
230
+ <rect x="6.5" y="1" width="4.5" height="4.5" rx="1" />
231
+ <rect x="1" y="6.5" width="4.5" height="4.5" rx="1" />
232
+ <rect x="6.5" y="6.5" width="4.5" height="4.5" rx="1" />
233
+ </svg>`;
234
+ export const stackIcon = () =>
235
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
236
+ <rect x="1" y="1" width="10" height="2.8" rx="0.5" />
237
+ <rect x="1" y="4.6" width="10" height="2.8" rx="0.5" />
238
+ <rect x="1" y="8.2" width="10" height="2.8" rx="0.5" />
239
+ </svg>`;
240
+ export const dualPipIcon = () =>
241
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
242
+ <rect x="1" y="1" width="10" height="10" rx="1" fill-opacity="0.3" />
243
+ <rect x="7" y="4" width="3.5" height="2.5" rx="0.5" />
244
+ <rect x="7" y="7" width="3.5" height="2.5" rx="0.5" />
245
+ </svg>`;
246
+ export const splitPipIcon = () =>
247
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
248
+ <rect x="1" y="1" width="4.5" height="10" rx="1" />
249
+ <rect x="6.5" y="1" width="4.5" height="10" rx="1" fill-opacity="0.5" />
250
+ <rect x="7.5" y="7" width="2.5" height="2.5" rx="0.5" />
251
+ </svg>`;
252
+ export const featuredIcon = () =>
253
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
254
+ <rect x="1" y="1" width="10" height="7.5" rx="1" />
255
+ <rect x="1" y="9" width="3" height="2" rx="0.5" fill-opacity="0.5" />
256
+ <rect x="4.5" y="9" width="3" height="2" rx="0.5" fill-opacity="0.5" />
257
+ <rect x="8" y="9" width="3" height="2" rx="0.5" fill-opacity="0.5" />
258
+ </svg>`;
259
+ export const featuredRIcon = () =>
260
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
261
+ <rect x="1" y="1" width="8" height="10" rx="1" />
262
+ <rect x="9.5" y="1" width="1.5" height="3" rx="0.5" fill-opacity="0.5" />
263
+ <rect x="9.5" y="4.5" width="1.5" height="3" rx="0.5" fill-opacity="0.5" />
264
+ <rect x="9.5" y="8" width="1.5" height="3" rx="0.5" fill-opacity="0.5" />
265
+ </svg>`;
266
+
267
+ // Scaling mode icons
268
+ export const letterboxIcon = () =>
269
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
270
+ <rect x="1" y="3" width="10" height="6" rx="1" />
271
+ <rect x="0" y="1" width="12" height="1.5" fill-opacity="0.3" />
272
+ <rect x="0" y="9.5" width="12" height="1.5" fill-opacity="0.3" />
273
+ </svg>`;
274
+ export const cropIcon = () =>
275
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
276
+ <rect x="0" y="0" width="12" height="12" rx="1" />
277
+ <path
278
+ d="M2 0v2H0v1h3V0H2zM10 0v3h2V2h-2V0H9v3h3V2h-2V0h1zM0 9v1h2v2h1V9H0zM12 9H9v3h1v-2h2v-1z"
279
+ fill-opacity="0.5"
280
+ />
281
+ </svg>`;
282
+ export const stretchIcon = () =>
283
+ html`<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
284
+ <rect x="1" y="1" width="10" height="10" rx="1" fill-opacity="0.3" />
285
+ <path
286
+ d="M3 5.5h6M3 5l-1.5 1L3 7M9 5l1.5 1L9 7M5.5 3v6M5 3L6 1.5 7 3M5 9l1 1.5 1-1.5"
287
+ stroke="currentColor"
288
+ stroke-width="1"
289
+ fill="none"
290
+ />
291
+ </svg>`;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * IIFE entry — bundles everything and auto-registers custom elements.
3
+ * For CDN <script> tag consumers.
4
+ *
5
+ * Usage:
6
+ * <script src="fw-streamcrafter.iife.js"></script>
7
+ * <fw-streamcrafter whip-url="..."></fw-streamcrafter>
8
+ */
9
+ import "./define.js";
10
+ export { FwStreamCrafter } from "./components/fw-streamcrafter.js";
11
+ export { IngestControllerHost } from "./controllers/ingest-controller-host.js";