@reactor-team/js-sdk 1.0.11 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +253 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +252 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
-
import
|
|
3
|
+
import React, { ReactNode } from 'react';
|
|
4
4
|
import { RemoteVideoTrack } from 'livekit-client';
|
|
5
5
|
|
|
6
6
|
type ReactorStatus = "disconnected" | "connecting" | "waiting" | "ready";
|
|
@@ -34,6 +34,7 @@ declare const OptionsSchema: z.ZodObject<{
|
|
|
34
34
|
coordinatorUrl: z.ZodDefault<z.ZodString>;
|
|
35
35
|
modelName: z.ZodString;
|
|
36
36
|
queueing: z.ZodDefault<z.ZodBoolean>;
|
|
37
|
+
local: z.ZodDefault<z.ZodBoolean>;
|
|
37
38
|
}, z.core.$strip>;
|
|
38
39
|
type Options = z.input<typeof OptionsSchema>;
|
|
39
40
|
type EventHandler = (...args: any[]) => void;
|
|
@@ -121,6 +122,8 @@ interface ReactorActions {
|
|
|
121
122
|
sendMessage(message: any): Promise<void>;
|
|
122
123
|
connect(): Promise<void>;
|
|
123
124
|
disconnect(): Promise<void>;
|
|
125
|
+
publishVideoStream(stream: MediaStream): Promise<void>;
|
|
126
|
+
unpublishVideoStream(): Promise<void>;
|
|
124
127
|
}
|
|
125
128
|
interface ReactorInternalState {
|
|
126
129
|
reactor: Reactor;
|
|
@@ -142,9 +145,19 @@ interface ReactorViewProps {
|
|
|
142
145
|
width?: number;
|
|
143
146
|
height?: number;
|
|
144
147
|
className?: string;
|
|
145
|
-
style?:
|
|
148
|
+
style?: React.CSSProperties;
|
|
149
|
+
videoObjectFit?: NonNullable<React.VideoHTMLAttributes<HTMLVideoElement>["style"]>["objectFit"];
|
|
150
|
+
}
|
|
151
|
+
declare function ReactorView({ width, height, className, style, videoObjectFit, }: ReactorViewProps): react_jsx_runtime.JSX.Element;
|
|
152
|
+
|
|
153
|
+
interface Props {
|
|
154
|
+
className?: string;
|
|
155
|
+
style?: React.CSSProperties;
|
|
156
|
+
videoConstraints?: MediaTrackConstraints;
|
|
157
|
+
showWebcam?: boolean;
|
|
158
|
+
videoObjectFit?: NonNullable<React.VideoHTMLAttributes<HTMLVideoElement>["style"]>["objectFit"];
|
|
146
159
|
}
|
|
147
|
-
declare function
|
|
160
|
+
declare function WebcamStream({ className, style, videoConstraints, showWebcam, videoObjectFit, }: Props): react_jsx_runtime.JSX.Element;
|
|
148
161
|
|
|
149
162
|
/**
|
|
150
163
|
* Generic hook for accessing selected parts of the Reactor store.
|
|
@@ -160,4 +173,4 @@ declare function useReactor<T>(selector: (state: ReactorStore) => T): T;
|
|
|
160
173
|
*/
|
|
161
174
|
declare function useReactorMessage(handler: (message: any) => void): void;
|
|
162
175
|
|
|
163
|
-
export { type Options, Reactor, type ReactorError, type ReactorEvent, ReactorProvider, type ReactorState$1 as ReactorState, type ReactorStatus, ReactorView, type ReactorViewProps, type ReactorWaitingInfo, useReactor, useReactorMessage, useReactorStore };
|
|
176
|
+
export { type Options, Reactor, type ReactorError, type ReactorEvent, ReactorProvider, type ReactorState$1 as ReactorState, type ReactorStatus, ReactorView, type ReactorViewProps, type ReactorWaitingInfo, WebcamStream, useReactor, useReactorMessage, useReactorStore };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
-
import
|
|
3
|
+
import React, { ReactNode } from 'react';
|
|
4
4
|
import { RemoteVideoTrack } from 'livekit-client';
|
|
5
5
|
|
|
6
6
|
type ReactorStatus = "disconnected" | "connecting" | "waiting" | "ready";
|
|
@@ -34,6 +34,7 @@ declare const OptionsSchema: z.ZodObject<{
|
|
|
34
34
|
coordinatorUrl: z.ZodDefault<z.ZodString>;
|
|
35
35
|
modelName: z.ZodString;
|
|
36
36
|
queueing: z.ZodDefault<z.ZodBoolean>;
|
|
37
|
+
local: z.ZodDefault<z.ZodBoolean>;
|
|
37
38
|
}, z.core.$strip>;
|
|
38
39
|
type Options = z.input<typeof OptionsSchema>;
|
|
39
40
|
type EventHandler = (...args: any[]) => void;
|
|
@@ -121,6 +122,8 @@ interface ReactorActions {
|
|
|
121
122
|
sendMessage(message: any): Promise<void>;
|
|
122
123
|
connect(): Promise<void>;
|
|
123
124
|
disconnect(): Promise<void>;
|
|
125
|
+
publishVideoStream(stream: MediaStream): Promise<void>;
|
|
126
|
+
unpublishVideoStream(): Promise<void>;
|
|
124
127
|
}
|
|
125
128
|
interface ReactorInternalState {
|
|
126
129
|
reactor: Reactor;
|
|
@@ -142,9 +145,19 @@ interface ReactorViewProps {
|
|
|
142
145
|
width?: number;
|
|
143
146
|
height?: number;
|
|
144
147
|
className?: string;
|
|
145
|
-
style?:
|
|
148
|
+
style?: React.CSSProperties;
|
|
149
|
+
videoObjectFit?: NonNullable<React.VideoHTMLAttributes<HTMLVideoElement>["style"]>["objectFit"];
|
|
150
|
+
}
|
|
151
|
+
declare function ReactorView({ width, height, className, style, videoObjectFit, }: ReactorViewProps): react_jsx_runtime.JSX.Element;
|
|
152
|
+
|
|
153
|
+
interface Props {
|
|
154
|
+
className?: string;
|
|
155
|
+
style?: React.CSSProperties;
|
|
156
|
+
videoConstraints?: MediaTrackConstraints;
|
|
157
|
+
showWebcam?: boolean;
|
|
158
|
+
videoObjectFit?: NonNullable<React.VideoHTMLAttributes<HTMLVideoElement>["style"]>["objectFit"];
|
|
146
159
|
}
|
|
147
|
-
declare function
|
|
160
|
+
declare function WebcamStream({ className, style, videoConstraints, showWebcam, videoObjectFit, }: Props): react_jsx_runtime.JSX.Element;
|
|
148
161
|
|
|
149
162
|
/**
|
|
150
163
|
* Generic hook for accessing selected parts of the Reactor store.
|
|
@@ -160,4 +173,4 @@ declare function useReactor<T>(selector: (state: ReactorStore) => T): T;
|
|
|
160
173
|
*/
|
|
161
174
|
declare function useReactorMessage(handler: (message: any) => void): void;
|
|
162
175
|
|
|
163
|
-
export { type Options, Reactor, type ReactorError, type ReactorEvent, ReactorProvider, type ReactorState$1 as ReactorState, type ReactorStatus, ReactorView, type ReactorViewProps, type ReactorWaitingInfo, useReactor, useReactorMessage, useReactorStore };
|
|
176
|
+
export { type Options, Reactor, type ReactorError, type ReactorEvent, ReactorProvider, type ReactorState$1 as ReactorState, type ReactorStatus, ReactorView, type ReactorViewProps, type ReactorWaitingInfo, WebcamStream, useReactor, useReactorMessage, useReactorStore };
|
package/dist/index.js
CHANGED
|
@@ -72,6 +72,7 @@ __export(index_exports, {
|
|
|
72
72
|
Reactor: () => Reactor,
|
|
73
73
|
ReactorProvider: () => ReactorProvider,
|
|
74
74
|
ReactorView: () => ReactorView,
|
|
75
|
+
WebcamStream: () => WebcamStream,
|
|
75
76
|
useReactor: () => useReactor,
|
|
76
77
|
useReactorMessage: () => useReactorMessage,
|
|
77
78
|
useReactorStore: () => useReactorStore
|
|
@@ -453,18 +454,22 @@ var GPUMachineClient = class {
|
|
|
453
454
|
}
|
|
454
455
|
/**
|
|
455
456
|
* Unpublishes the currently published video track.
|
|
457
|
+
* Note: We pass false to unpublishTrack to prevent LiveKit from stopping
|
|
458
|
+
* the source MediaStreamTrack, as it's owned by the component that created it.
|
|
456
459
|
*/
|
|
457
460
|
unpublishVideoTrack() {
|
|
458
461
|
return __async(this, null, function* () {
|
|
459
462
|
if (!this.roomInstance || !this.publishedVideoTrack) {
|
|
460
463
|
return;
|
|
461
464
|
}
|
|
465
|
+
const publishedVideoTrack = this.publishedVideoTrack;
|
|
466
|
+
this.publishedVideoTrack = void 0;
|
|
462
467
|
try {
|
|
463
468
|
yield this.roomInstance.localParticipant.unpublishTrack(
|
|
464
|
-
|
|
469
|
+
publishedVideoTrack,
|
|
470
|
+
false
|
|
471
|
+
// Don't stop the source track - it's managed externally
|
|
465
472
|
);
|
|
466
|
-
this.publishedVideoTrack.stop();
|
|
467
|
-
this.publishedVideoTrack = void 0;
|
|
468
473
|
console.debug("[GPUMachineClient] Video track unpublished successfully");
|
|
469
474
|
} catch (error) {
|
|
470
475
|
console.error(
|
|
@@ -512,6 +517,8 @@ var GPUMachineClient = class {
|
|
|
512
517
|
}
|
|
513
518
|
/**
|
|
514
519
|
* Unpublishes the currently published audio track.
|
|
520
|
+
* Note: We pass false to unpublishTrack to prevent LiveKit from stopping
|
|
521
|
+
* the source MediaStreamTrack, as it's owned by the component that created it.
|
|
515
522
|
* @private
|
|
516
523
|
*/
|
|
517
524
|
unpublishAudioTrack() {
|
|
@@ -519,12 +526,14 @@ var GPUMachineClient = class {
|
|
|
519
526
|
if (!this.roomInstance || !this.publishedAudioTrack) {
|
|
520
527
|
return;
|
|
521
528
|
}
|
|
529
|
+
const publishedAudioTrack = this.publishedAudioTrack;
|
|
530
|
+
this.publishedAudioTrack = void 0;
|
|
522
531
|
try {
|
|
523
532
|
yield this.roomInstance.localParticipant.unpublishTrack(
|
|
524
|
-
|
|
533
|
+
publishedAudioTrack,
|
|
534
|
+
false
|
|
535
|
+
// Don't stop the source track - it's managed externally
|
|
525
536
|
);
|
|
526
|
-
this.publishedAudioTrack.stop();
|
|
527
|
-
this.publishedAudioTrack = void 0;
|
|
528
537
|
console.debug("[GPUMachineClient] Audio track unpublished successfully");
|
|
529
538
|
} catch (error) {
|
|
530
539
|
console.error(
|
|
@@ -539,6 +548,9 @@ var GPUMachineClient = class {
|
|
|
539
548
|
|
|
540
549
|
// src/core/Reactor.ts
|
|
541
550
|
var import_zod3 = require("zod");
|
|
551
|
+
var LOCAL_COORDINATOR_URL = "ws://localhost:8080/ws";
|
|
552
|
+
var LOCAL_INSECURE_API_KEY = "1234";
|
|
553
|
+
var PROD_COORDINATOR_URL = "wss://api.reactor.inc/ws";
|
|
542
554
|
var OptionsSchema2 = import_zod3.z.object({
|
|
543
555
|
directConnection: import_zod3.z.object({
|
|
544
556
|
livekitJwtToken: import_zod3.z.string(),
|
|
@@ -546,13 +558,14 @@ var OptionsSchema2 = import_zod3.z.object({
|
|
|
546
558
|
}).optional(),
|
|
547
559
|
insecureApiKey: import_zod3.z.string().optional(),
|
|
548
560
|
jwtToken: import_zod3.z.string().optional(),
|
|
549
|
-
coordinatorUrl: import_zod3.z.string().default(
|
|
561
|
+
coordinatorUrl: import_zod3.z.string().default(PROD_COORDINATOR_URL),
|
|
550
562
|
modelName: import_zod3.z.string(),
|
|
551
|
-
queueing: import_zod3.z.boolean().default(false)
|
|
563
|
+
queueing: import_zod3.z.boolean().default(false),
|
|
564
|
+
local: import_zod3.z.boolean().default(false)
|
|
552
565
|
}).refine(
|
|
553
|
-
(data) => data.directConnection || data.insecureApiKey || data.jwtToken,
|
|
566
|
+
(data) => data.directConnection || data.insecureApiKey || data.jwtToken || data.local,
|
|
554
567
|
{
|
|
555
|
-
message: "At least one of directConnection, insecureApiKey, or jwtToken must be provided."
|
|
568
|
+
message: "At least one of directConnection, insecureApiKey, or jwtToken or local must be provided."
|
|
556
569
|
}
|
|
557
570
|
);
|
|
558
571
|
var Reactor = class {
|
|
@@ -569,6 +582,10 @@ var Reactor = class {
|
|
|
569
582
|
this.modelName = validatedOptions.modelName;
|
|
570
583
|
this.queueing = validatedOptions.queueing;
|
|
571
584
|
this.modelVersion = "1.0.0";
|
|
585
|
+
if (validatedOptions.local) {
|
|
586
|
+
this.coordinatorUrl = LOCAL_COORDINATOR_URL;
|
|
587
|
+
this.insecureApiKey = LOCAL_INSECURE_API_KEY;
|
|
588
|
+
}
|
|
572
589
|
}
|
|
573
590
|
// Event Emitter API
|
|
574
591
|
on(event, handler) {
|
|
@@ -996,6 +1013,32 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
|
|
|
996
1013
|
console.error("[ReactorStore] Disconnect failed:", error);
|
|
997
1014
|
throw error;
|
|
998
1015
|
}
|
|
1016
|
+
}),
|
|
1017
|
+
publishVideoStream: (stream) => __async(null, null, function* () {
|
|
1018
|
+
console.debug("[ReactorStore] Publishing video stream");
|
|
1019
|
+
try {
|
|
1020
|
+
yield get().internal.reactor.publishVideoStream(stream);
|
|
1021
|
+
console.debug("[ReactorStore] Video stream published successfully");
|
|
1022
|
+
} catch (error) {
|
|
1023
|
+
console.error(
|
|
1024
|
+
"[ReactorStore] Failed to publish video stream:",
|
|
1025
|
+
error
|
|
1026
|
+
);
|
|
1027
|
+
throw error;
|
|
1028
|
+
}
|
|
1029
|
+
}),
|
|
1030
|
+
unpublishVideoStream: () => __async(null, null, function* () {
|
|
1031
|
+
console.debug("[ReactorStore] Unpublishing video stream");
|
|
1032
|
+
try {
|
|
1033
|
+
yield get().internal.reactor.unpublishVideoStream();
|
|
1034
|
+
console.debug("[ReactorStore] Video stream unpublished successfully");
|
|
1035
|
+
} catch (error) {
|
|
1036
|
+
console.error(
|
|
1037
|
+
"[ReactorStore] Failed to unpublish video stream:",
|
|
1038
|
+
error
|
|
1039
|
+
);
|
|
1040
|
+
throw error;
|
|
1041
|
+
}
|
|
999
1042
|
})
|
|
1000
1043
|
});
|
|
1001
1044
|
});
|
|
@@ -1026,7 +1069,8 @@ function ReactorProvider(_a) {
|
|
|
1026
1069
|
jwtToken,
|
|
1027
1070
|
insecureApiKey,
|
|
1028
1071
|
directConnection,
|
|
1029
|
-
queueing
|
|
1072
|
+
queueing,
|
|
1073
|
+
local
|
|
1030
1074
|
} = props;
|
|
1031
1075
|
(0, import_react3.useEffect)(() => {
|
|
1032
1076
|
if (firstRender.current) {
|
|
@@ -1071,7 +1115,8 @@ function ReactorProvider(_a) {
|
|
|
1071
1115
|
jwtToken,
|
|
1072
1116
|
insecureApiKey,
|
|
1073
1117
|
directConnection,
|
|
1074
|
-
queueing
|
|
1118
|
+
queueing,
|
|
1119
|
+
local
|
|
1075
1120
|
})
|
|
1076
1121
|
);
|
|
1077
1122
|
const current = storeRef.current;
|
|
@@ -1104,7 +1149,8 @@ function ReactorProvider(_a) {
|
|
|
1104
1149
|
insecureApiKey,
|
|
1105
1150
|
directConnection,
|
|
1106
1151
|
queueing,
|
|
1107
|
-
autoConnect
|
|
1152
|
+
autoConnect,
|
|
1153
|
+
local
|
|
1108
1154
|
]);
|
|
1109
1155
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReactorContext.Provider, { value: storeRef.current, children });
|
|
1110
1156
|
}
|
|
@@ -1150,18 +1196,14 @@ function ReactorView({
|
|
|
1150
1196
|
width,
|
|
1151
1197
|
height,
|
|
1152
1198
|
className,
|
|
1153
|
-
style
|
|
1199
|
+
style,
|
|
1200
|
+
videoObjectFit = "contain"
|
|
1154
1201
|
}) {
|
|
1155
1202
|
const { videoTrack, status } = useReactor((state) => ({
|
|
1156
1203
|
videoTrack: state.videoTrack,
|
|
1157
1204
|
status: state.status
|
|
1158
1205
|
}));
|
|
1159
1206
|
const videoRef = (0, import_react5.useRef)(null);
|
|
1160
|
-
console.debug("[ReactorView] Render", {
|
|
1161
|
-
hasVideoTrack: !!videoTrack,
|
|
1162
|
-
status,
|
|
1163
|
-
hasVideoElement: !!videoRef.current
|
|
1164
|
-
});
|
|
1165
1207
|
(0, import_react5.useEffect)(() => {
|
|
1166
1208
|
console.debug("[ReactorView] Video track effect triggered", {
|
|
1167
1209
|
hasVideoElement: !!videoRef.current,
|
|
@@ -1192,7 +1234,6 @@ function ReactorView({
|
|
|
1192
1234
|
}
|
|
1193
1235
|
}, [videoTrack]);
|
|
1194
1236
|
const showPlaceholder = !videoTrack;
|
|
1195
|
-
console.debug("[ReactorView] Placeholder state", { showPlaceholder, status });
|
|
1196
1237
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1197
1238
|
"div",
|
|
1198
1239
|
{
|
|
@@ -1209,7 +1250,7 @@ function ReactorView({
|
|
|
1209
1250
|
style: {
|
|
1210
1251
|
width: "100%",
|
|
1211
1252
|
height: "100%",
|
|
1212
|
-
objectFit:
|
|
1253
|
+
objectFit: videoObjectFit,
|
|
1213
1254
|
display: showPlaceholder ? "none" : "block"
|
|
1214
1255
|
},
|
|
1215
1256
|
muted: true,
|
|
@@ -1242,11 +1283,202 @@ function ReactorView({
|
|
|
1242
1283
|
}
|
|
1243
1284
|
);
|
|
1244
1285
|
}
|
|
1286
|
+
|
|
1287
|
+
// src/react/WebcamStream.tsx
|
|
1288
|
+
var import_react6 = require("react");
|
|
1289
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1290
|
+
function WebcamStream({
|
|
1291
|
+
className,
|
|
1292
|
+
style,
|
|
1293
|
+
videoConstraints = {
|
|
1294
|
+
width: { ideal: 1280 },
|
|
1295
|
+
height: { ideal: 720 }
|
|
1296
|
+
},
|
|
1297
|
+
showWebcam = true,
|
|
1298
|
+
videoObjectFit = "contain"
|
|
1299
|
+
}) {
|
|
1300
|
+
const [stream, setStream] = (0, import_react6.useState)(null);
|
|
1301
|
+
const [isPublishing, setIsPublishing] = (0, import_react6.useState)(false);
|
|
1302
|
+
const [permissionDenied, setPermissionDenied] = (0, import_react6.useState)(false);
|
|
1303
|
+
const { status, publishVideoStream, unpublishVideoStream, reactor } = useReactor((state) => ({
|
|
1304
|
+
status: state.status,
|
|
1305
|
+
publishVideoStream: state.publishVideoStream,
|
|
1306
|
+
unpublishVideoStream: state.unpublishVideoStream,
|
|
1307
|
+
reactor: state.internal.reactor
|
|
1308
|
+
}));
|
|
1309
|
+
const videoRef = (0, import_react6.useRef)(null);
|
|
1310
|
+
const startWebcam = () => __async(null, null, function* () {
|
|
1311
|
+
console.debug("[WebcamPublisher] Starting webcam");
|
|
1312
|
+
try {
|
|
1313
|
+
const mediaStream = yield navigator.mediaDevices.getUserMedia({
|
|
1314
|
+
video: videoConstraints,
|
|
1315
|
+
audio: false
|
|
1316
|
+
});
|
|
1317
|
+
console.debug("[WebcamPublisher] Webcam started successfully");
|
|
1318
|
+
setStream(mediaStream);
|
|
1319
|
+
setPermissionDenied(false);
|
|
1320
|
+
} catch (err) {
|
|
1321
|
+
console.error("[WebcamPublisher] Failed to start webcam:", err);
|
|
1322
|
+
if (err instanceof DOMException && (err.name === "NotAllowedError" || err.name === "PermissionDeniedError")) {
|
|
1323
|
+
console.debug("[WebcamPublisher] Camera permission denied");
|
|
1324
|
+
setPermissionDenied(true);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
});
|
|
1328
|
+
const stopWebcam = () => __async(null, null, function* () {
|
|
1329
|
+
console.debug("[WebcamPublisher] Stopping webcam");
|
|
1330
|
+
try {
|
|
1331
|
+
yield unpublishVideoStream();
|
|
1332
|
+
console.debug("[WebcamPublisher] Unpublished before stopping");
|
|
1333
|
+
} catch (err) {
|
|
1334
|
+
console.error("[WebcamPublisher] Error unpublishing before stop:", err);
|
|
1335
|
+
}
|
|
1336
|
+
setIsPublishing(false);
|
|
1337
|
+
stream == null ? void 0 : stream.getTracks().forEach((track) => {
|
|
1338
|
+
track.stop();
|
|
1339
|
+
console.debug("[WebcamPublisher] Stopped track:", track.kind);
|
|
1340
|
+
});
|
|
1341
|
+
setStream(null);
|
|
1342
|
+
console.debug("[WebcamPublisher] Webcam stopped");
|
|
1343
|
+
});
|
|
1344
|
+
(0, import_react6.useEffect)(() => {
|
|
1345
|
+
console.debug("[WebcamPublisher] Stream effect triggered", {
|
|
1346
|
+
hasVideoElement: !!videoRef.current,
|
|
1347
|
+
hasStream: !!stream
|
|
1348
|
+
});
|
|
1349
|
+
if (!videoRef.current) {
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
if (stream) {
|
|
1353
|
+
console.debug("[WebcamPublisher] Attaching stream to video element");
|
|
1354
|
+
videoRef.current.srcObject = stream;
|
|
1355
|
+
console.debug("[WebcamPublisher] Stream attached successfully");
|
|
1356
|
+
} else {
|
|
1357
|
+
console.debug("[WebcamPublisher] Clearing video element");
|
|
1358
|
+
videoRef.current.srcObject = null;
|
|
1359
|
+
}
|
|
1360
|
+
}, [stream]);
|
|
1361
|
+
(0, import_react6.useEffect)(() => {
|
|
1362
|
+
if (!stream) {
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
if (status === "ready" && !isPublishing) {
|
|
1366
|
+
console.debug(
|
|
1367
|
+
"[WebcamPublisher] Reactor ready, auto-publishing webcam stream"
|
|
1368
|
+
);
|
|
1369
|
+
publishVideoStream(stream).then(() => {
|
|
1370
|
+
console.debug("[WebcamPublisher] Auto-publish successful");
|
|
1371
|
+
setIsPublishing(true);
|
|
1372
|
+
}).catch((err) => {
|
|
1373
|
+
console.error("[WebcamPublisher] Auto-publish failed:", err);
|
|
1374
|
+
});
|
|
1375
|
+
} else if (status !== "ready" && isPublishing) {
|
|
1376
|
+
console.debug("[WebcamPublisher] Reactor not ready, auto-unpublishing");
|
|
1377
|
+
unpublishVideoStream().then(() => {
|
|
1378
|
+
console.debug("[WebcamPublisher] Auto-unpublish successful");
|
|
1379
|
+
setIsPublishing(false);
|
|
1380
|
+
}).catch((err) => {
|
|
1381
|
+
console.error("[WebcamPublisher] Auto-unpublish failed:", err);
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
}, [status, stream, isPublishing, publishVideoStream, unpublishVideoStream]);
|
|
1385
|
+
(0, import_react6.useEffect)(() => {
|
|
1386
|
+
const handleError = (error) => {
|
|
1387
|
+
console.debug("[WebcamPublisher] Received error event:", error);
|
|
1388
|
+
if (error.code === "VIDEO_PUBLISH_FAILED") {
|
|
1389
|
+
console.debug(
|
|
1390
|
+
"[WebcamPublisher] Video publish failed, resetting isPublishing state"
|
|
1391
|
+
);
|
|
1392
|
+
setIsPublishing(false);
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
reactor.on("error", handleError);
|
|
1396
|
+
return () => {
|
|
1397
|
+
reactor.off("error", handleError);
|
|
1398
|
+
};
|
|
1399
|
+
}, [reactor]);
|
|
1400
|
+
(0, import_react6.useEffect)(() => {
|
|
1401
|
+
if (status !== "ready") {
|
|
1402
|
+
console.debug(
|
|
1403
|
+
"[WebcamPublisher] Status changed to",
|
|
1404
|
+
status,
|
|
1405
|
+
"- resetting isPublishing state"
|
|
1406
|
+
);
|
|
1407
|
+
setIsPublishing(false);
|
|
1408
|
+
}
|
|
1409
|
+
}, [status, isPublishing]);
|
|
1410
|
+
(0, import_react6.useEffect)(() => {
|
|
1411
|
+
console.debug("[WebcamPublisher] Auto-starting webcam");
|
|
1412
|
+
startWebcam();
|
|
1413
|
+
return () => {
|
|
1414
|
+
console.debug("[WebcamPublisher] Cleanup on unmount");
|
|
1415
|
+
stopWebcam();
|
|
1416
|
+
};
|
|
1417
|
+
}, []);
|
|
1418
|
+
const showPlaceholder = !stream;
|
|
1419
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1420
|
+
"div",
|
|
1421
|
+
{
|
|
1422
|
+
style: __spreadValues({
|
|
1423
|
+
display: showWebcam ? "block" : "none",
|
|
1424
|
+
position: "relative",
|
|
1425
|
+
background: "#000"
|
|
1426
|
+
}, style),
|
|
1427
|
+
className,
|
|
1428
|
+
children: [
|
|
1429
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1430
|
+
"video",
|
|
1431
|
+
{
|
|
1432
|
+
ref: videoRef,
|
|
1433
|
+
style: {
|
|
1434
|
+
width: "100%",
|
|
1435
|
+
height: "100%",
|
|
1436
|
+
objectFit: videoObjectFit,
|
|
1437
|
+
display: showPlaceholder ? "none" : "block"
|
|
1438
|
+
},
|
|
1439
|
+
muted: true,
|
|
1440
|
+
playsInline: true,
|
|
1441
|
+
autoPlay: true
|
|
1442
|
+
}
|
|
1443
|
+
),
|
|
1444
|
+
showPlaceholder && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1445
|
+
"div",
|
|
1446
|
+
{
|
|
1447
|
+
style: {
|
|
1448
|
+
position: "absolute",
|
|
1449
|
+
top: 0,
|
|
1450
|
+
left: 0,
|
|
1451
|
+
width: "100%",
|
|
1452
|
+
height: "100%",
|
|
1453
|
+
color: "#fff",
|
|
1454
|
+
display: "flex",
|
|
1455
|
+
alignItems: "center",
|
|
1456
|
+
justifyContent: "center",
|
|
1457
|
+
fontSize: "16px",
|
|
1458
|
+
fontFamily: "monospace",
|
|
1459
|
+
textAlign: "center",
|
|
1460
|
+
padding: "20px",
|
|
1461
|
+
boxSizing: "border-box",
|
|
1462
|
+
flexDirection: "column",
|
|
1463
|
+
gap: "12px"
|
|
1464
|
+
},
|
|
1465
|
+
children: permissionDenied ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: "12px", fontFamily: "monospace" }, children: [
|
|
1466
|
+
"Camera access denied.",
|
|
1467
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("br", {}),
|
|
1468
|
+
"Please allow access in your browser settings."
|
|
1469
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontSize: "12px", fontFamily: "monospace" }, children: "Starting camera..." })
|
|
1470
|
+
}
|
|
1471
|
+
)
|
|
1472
|
+
]
|
|
1473
|
+
}
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1245
1476
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1246
1477
|
0 && (module.exports = {
|
|
1247
1478
|
Reactor,
|
|
1248
1479
|
ReactorProvider,
|
|
1249
1480
|
ReactorView,
|
|
1481
|
+
WebcamStream,
|
|
1250
1482
|
useReactor,
|
|
1251
1483
|
useReactorMessage,
|
|
1252
1484
|
useReactorStore
|