@reactor-team/js-sdk 1.0.10 → 1.0.14
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 +16 -4
- package/dist/index.d.ts +16 -4
- package/dist/index.js +317 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +317 -33
- 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";
|
|
@@ -121,6 +121,8 @@ interface ReactorActions {
|
|
|
121
121
|
sendMessage(message: any): Promise<void>;
|
|
122
122
|
connect(): Promise<void>;
|
|
123
123
|
disconnect(): Promise<void>;
|
|
124
|
+
publishVideoStream(stream: MediaStream): Promise<void>;
|
|
125
|
+
unpublishVideoStream(): Promise<void>;
|
|
124
126
|
}
|
|
125
127
|
interface ReactorInternalState {
|
|
126
128
|
reactor: Reactor;
|
|
@@ -142,9 +144,19 @@ interface ReactorViewProps {
|
|
|
142
144
|
width?: number;
|
|
143
145
|
height?: number;
|
|
144
146
|
className?: string;
|
|
145
|
-
style?:
|
|
147
|
+
style?: React.CSSProperties;
|
|
148
|
+
videoObjectFit?: NonNullable<React.VideoHTMLAttributes<HTMLVideoElement>["style"]>["objectFit"];
|
|
149
|
+
}
|
|
150
|
+
declare function ReactorView({ width, height, className, style, videoObjectFit, }: ReactorViewProps): react_jsx_runtime.JSX.Element;
|
|
151
|
+
|
|
152
|
+
interface Props {
|
|
153
|
+
className?: string;
|
|
154
|
+
style?: React.CSSProperties;
|
|
155
|
+
videoConstraints?: MediaTrackConstraints;
|
|
156
|
+
showWebcam?: boolean;
|
|
157
|
+
videoObjectFit?: NonNullable<React.VideoHTMLAttributes<HTMLVideoElement>["style"]>["objectFit"];
|
|
146
158
|
}
|
|
147
|
-
declare function
|
|
159
|
+
declare function WebcamStream({ className, style, videoConstraints, showWebcam, videoObjectFit, }: Props): react_jsx_runtime.JSX.Element;
|
|
148
160
|
|
|
149
161
|
/**
|
|
150
162
|
* Generic hook for accessing selected parts of the Reactor store.
|
|
@@ -160,4 +172,4 @@ declare function useReactor<T>(selector: (state: ReactorStore) => T): T;
|
|
|
160
172
|
*/
|
|
161
173
|
declare function useReactorMessage(handler: (message: any) => void): void;
|
|
162
174
|
|
|
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 };
|
|
175
|
+
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";
|
|
@@ -121,6 +121,8 @@ interface ReactorActions {
|
|
|
121
121
|
sendMessage(message: any): Promise<void>;
|
|
122
122
|
connect(): Promise<void>;
|
|
123
123
|
disconnect(): Promise<void>;
|
|
124
|
+
publishVideoStream(stream: MediaStream): Promise<void>;
|
|
125
|
+
unpublishVideoStream(): Promise<void>;
|
|
124
126
|
}
|
|
125
127
|
interface ReactorInternalState {
|
|
126
128
|
reactor: Reactor;
|
|
@@ -142,9 +144,19 @@ interface ReactorViewProps {
|
|
|
142
144
|
width?: number;
|
|
143
145
|
height?: number;
|
|
144
146
|
className?: string;
|
|
145
|
-
style?:
|
|
147
|
+
style?: React.CSSProperties;
|
|
148
|
+
videoObjectFit?: NonNullable<React.VideoHTMLAttributes<HTMLVideoElement>["style"]>["objectFit"];
|
|
149
|
+
}
|
|
150
|
+
declare function ReactorView({ width, height, className, style, videoObjectFit, }: ReactorViewProps): react_jsx_runtime.JSX.Element;
|
|
151
|
+
|
|
152
|
+
interface Props {
|
|
153
|
+
className?: string;
|
|
154
|
+
style?: React.CSSProperties;
|
|
155
|
+
videoConstraints?: MediaTrackConstraints;
|
|
156
|
+
showWebcam?: boolean;
|
|
157
|
+
videoObjectFit?: NonNullable<React.VideoHTMLAttributes<HTMLVideoElement>["style"]>["objectFit"];
|
|
146
158
|
}
|
|
147
|
-
declare function
|
|
159
|
+
declare function WebcamStream({ className, style, videoConstraints, showWebcam, videoObjectFit, }: Props): react_jsx_runtime.JSX.Element;
|
|
148
160
|
|
|
149
161
|
/**
|
|
150
162
|
* Generic hook for accessing selected parts of the Reactor store.
|
|
@@ -160,4 +172,4 @@ declare function useReactor<T>(selector: (state: ReactorStore) => T): T;
|
|
|
160
172
|
*/
|
|
161
173
|
declare function useReactorMessage(handler: (message: any) => void): void;
|
|
162
174
|
|
|
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 };
|
|
175
|
+
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(
|
|
@@ -996,6 +1005,32 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
|
|
|
996
1005
|
console.error("[ReactorStore] Disconnect failed:", error);
|
|
997
1006
|
throw error;
|
|
998
1007
|
}
|
|
1008
|
+
}),
|
|
1009
|
+
publishVideoStream: (stream) => __async(null, null, function* () {
|
|
1010
|
+
console.debug("[ReactorStore] Publishing video stream");
|
|
1011
|
+
try {
|
|
1012
|
+
yield get().internal.reactor.publishVideoStream(stream);
|
|
1013
|
+
console.debug("[ReactorStore] Video stream published successfully");
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
console.error(
|
|
1016
|
+
"[ReactorStore] Failed to publish video stream:",
|
|
1017
|
+
error
|
|
1018
|
+
);
|
|
1019
|
+
throw error;
|
|
1020
|
+
}
|
|
1021
|
+
}),
|
|
1022
|
+
unpublishVideoStream: () => __async(null, null, function* () {
|
|
1023
|
+
console.debug("[ReactorStore] Unpublishing video stream");
|
|
1024
|
+
try {
|
|
1025
|
+
yield get().internal.reactor.unpublishVideoStream();
|
|
1026
|
+
console.debug("[ReactorStore] Video stream unpublished successfully");
|
|
1027
|
+
} catch (error) {
|
|
1028
|
+
console.error(
|
|
1029
|
+
"[ReactorStore] Failed to unpublish video stream:",
|
|
1030
|
+
error
|
|
1031
|
+
);
|
|
1032
|
+
throw error;
|
|
1033
|
+
}
|
|
999
1034
|
})
|
|
1000
1035
|
});
|
|
1001
1036
|
});
|
|
@@ -1014,34 +1049,98 @@ function ReactorProvider(_a) {
|
|
|
1014
1049
|
]);
|
|
1015
1050
|
const storeRef = (0, import_react3.useRef)(void 0);
|
|
1016
1051
|
const firstRender = (0, import_react3.useRef)(true);
|
|
1017
|
-
|
|
1018
|
-
var _a2, _b2;
|
|
1019
|
-
const status = (_a2 = storeRef.current) == null ? void 0 : _a2.getState().status;
|
|
1020
|
-
if (autoConnect && status === "disconnected") {
|
|
1021
|
-
console.debug("[ReactorProvider] Starting autoconnect...");
|
|
1022
|
-
(_b2 = storeRef.current) == null ? void 0 : _b2.getState().connect().then(() => {
|
|
1023
|
-
console.debug("[ReactorProvider] Autoconnect successful");
|
|
1024
|
-
}).catch((error) => {
|
|
1025
|
-
console.error("[ReactorProvider] Failed to autoconnect:", error);
|
|
1026
|
-
});
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1052
|
+
const [_storeVersion, setStoreVersion] = (0, import_react3.useState)(0);
|
|
1029
1053
|
if (storeRef.current === void 0) {
|
|
1030
1054
|
console.debug("[ReactorProvider] Creating new reactor store");
|
|
1031
1055
|
storeRef.current = createReactorStore(initReactorStore(props));
|
|
1032
1056
|
console.debug("[ReactorProvider] Reactor store created successfully");
|
|
1033
|
-
attemptAutoConnect();
|
|
1034
1057
|
}
|
|
1058
|
+
const {
|
|
1059
|
+
coordinatorUrl,
|
|
1060
|
+
modelName,
|
|
1061
|
+
jwtToken,
|
|
1062
|
+
insecureApiKey,
|
|
1063
|
+
directConnection,
|
|
1064
|
+
queueing
|
|
1065
|
+
} = props;
|
|
1035
1066
|
(0, import_react3.useEffect)(() => {
|
|
1036
1067
|
if (firstRender.current) {
|
|
1037
1068
|
firstRender.current = false;
|
|
1038
|
-
|
|
1069
|
+
const current2 = storeRef.current;
|
|
1070
|
+
if (autoConnect && current2.getState().status === "disconnected") {
|
|
1071
|
+
console.debug(
|
|
1072
|
+
"[ReactorProvider] Starting autoconnect in first render..."
|
|
1073
|
+
);
|
|
1074
|
+
current2.getState().connect().then(() => {
|
|
1075
|
+
console.debug(
|
|
1076
|
+
"[ReactorProvider] Autoconnect successful in first render"
|
|
1077
|
+
);
|
|
1078
|
+
}).catch((error) => {
|
|
1079
|
+
console.error(
|
|
1080
|
+
"[ReactorProvider] Failed to autoconnect in first render:",
|
|
1081
|
+
error
|
|
1082
|
+
);
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
return () => {
|
|
1086
|
+
console.debug(
|
|
1087
|
+
"[ReactorProvider] Disconnecting in cleanup for first render"
|
|
1088
|
+
);
|
|
1089
|
+
current2.getState().disconnect().then(() => {
|
|
1090
|
+
console.debug(
|
|
1091
|
+
"[ReactorProvider] Disconnect completed successfully in cleanup for first render"
|
|
1092
|
+
);
|
|
1093
|
+
}).catch((error) => {
|
|
1094
|
+
console.error(
|
|
1095
|
+
"[ReactorProvider] Failed to disconnect in cleanup for first render:",
|
|
1096
|
+
error
|
|
1097
|
+
);
|
|
1098
|
+
});
|
|
1099
|
+
};
|
|
1039
1100
|
}
|
|
1040
1101
|
console.debug("[ReactorProvider] Updating reactor store");
|
|
1041
|
-
storeRef.current = createReactorStore(
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1102
|
+
storeRef.current = createReactorStore(
|
|
1103
|
+
initReactorStore({
|
|
1104
|
+
coordinatorUrl,
|
|
1105
|
+
modelName,
|
|
1106
|
+
jwtToken,
|
|
1107
|
+
insecureApiKey,
|
|
1108
|
+
directConnection,
|
|
1109
|
+
queueing
|
|
1110
|
+
})
|
|
1111
|
+
);
|
|
1112
|
+
const current = storeRef.current;
|
|
1113
|
+
setStoreVersion((v) => v + 1);
|
|
1114
|
+
console.debug(
|
|
1115
|
+
"[ReactorProvider] Reactor store updated successfully, and increased version"
|
|
1116
|
+
);
|
|
1117
|
+
if (autoConnect && current.getState().status === "disconnected") {
|
|
1118
|
+
console.debug("[ReactorProvider] Starting autoconnect...");
|
|
1119
|
+
current.getState().connect().then(() => {
|
|
1120
|
+
console.debug("[ReactorProvider] Autoconnect successful");
|
|
1121
|
+
}).catch((error) => {
|
|
1122
|
+
console.error("[ReactorProvider] Failed to autoconnect:", error);
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
return () => {
|
|
1126
|
+
console.debug("[ReactorProvider] Disconnecting in cleanup");
|
|
1127
|
+
current.getState().disconnect().then(() => {
|
|
1128
|
+
console.debug(
|
|
1129
|
+
"[ReactorProvider] Disconnect completed successfully in cleanup"
|
|
1130
|
+
);
|
|
1131
|
+
}).catch((error) => {
|
|
1132
|
+
console.error("[ReactorProvider] Failed to disconnect:", error);
|
|
1133
|
+
});
|
|
1134
|
+
};
|
|
1135
|
+
}, [
|
|
1136
|
+
coordinatorUrl,
|
|
1137
|
+
modelName,
|
|
1138
|
+
jwtToken,
|
|
1139
|
+
insecureApiKey,
|
|
1140
|
+
directConnection,
|
|
1141
|
+
queueing,
|
|
1142
|
+
autoConnect
|
|
1143
|
+
]);
|
|
1045
1144
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReactorContext.Provider, { value: storeRef.current, children });
|
|
1046
1145
|
}
|
|
1047
1146
|
function useReactorStore(selector) {
|
|
@@ -1086,18 +1185,14 @@ function ReactorView({
|
|
|
1086
1185
|
width,
|
|
1087
1186
|
height,
|
|
1088
1187
|
className,
|
|
1089
|
-
style
|
|
1188
|
+
style,
|
|
1189
|
+
videoObjectFit = "contain"
|
|
1090
1190
|
}) {
|
|
1091
1191
|
const { videoTrack, status } = useReactor((state) => ({
|
|
1092
1192
|
videoTrack: state.videoTrack,
|
|
1093
1193
|
status: state.status
|
|
1094
1194
|
}));
|
|
1095
1195
|
const videoRef = (0, import_react5.useRef)(null);
|
|
1096
|
-
console.debug("[ReactorView] Render", {
|
|
1097
|
-
hasVideoTrack: !!videoTrack,
|
|
1098
|
-
status,
|
|
1099
|
-
hasVideoElement: !!videoRef.current
|
|
1100
|
-
});
|
|
1101
1196
|
(0, import_react5.useEffect)(() => {
|
|
1102
1197
|
console.debug("[ReactorView] Video track effect triggered", {
|
|
1103
1198
|
hasVideoElement: !!videoRef.current,
|
|
@@ -1128,7 +1223,6 @@ function ReactorView({
|
|
|
1128
1223
|
}
|
|
1129
1224
|
}, [videoTrack]);
|
|
1130
1225
|
const showPlaceholder = !videoTrack;
|
|
1131
|
-
console.debug("[ReactorView] Placeholder state", { showPlaceholder, status });
|
|
1132
1226
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1133
1227
|
"div",
|
|
1134
1228
|
{
|
|
@@ -1145,7 +1239,7 @@ function ReactorView({
|
|
|
1145
1239
|
style: {
|
|
1146
1240
|
width: "100%",
|
|
1147
1241
|
height: "100%",
|
|
1148
|
-
objectFit:
|
|
1242
|
+
objectFit: videoObjectFit,
|
|
1149
1243
|
display: showPlaceholder ? "none" : "block"
|
|
1150
1244
|
},
|
|
1151
1245
|
muted: true,
|
|
@@ -1178,11 +1272,202 @@ function ReactorView({
|
|
|
1178
1272
|
}
|
|
1179
1273
|
);
|
|
1180
1274
|
}
|
|
1275
|
+
|
|
1276
|
+
// src/react/WebcamStream.tsx
|
|
1277
|
+
var import_react6 = require("react");
|
|
1278
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1279
|
+
function WebcamStream({
|
|
1280
|
+
className,
|
|
1281
|
+
style,
|
|
1282
|
+
videoConstraints = {
|
|
1283
|
+
width: { ideal: 1280 },
|
|
1284
|
+
height: { ideal: 720 }
|
|
1285
|
+
},
|
|
1286
|
+
showWebcam = true,
|
|
1287
|
+
videoObjectFit = "contain"
|
|
1288
|
+
}) {
|
|
1289
|
+
const [stream, setStream] = (0, import_react6.useState)(null);
|
|
1290
|
+
const [isPublishing, setIsPublishing] = (0, import_react6.useState)(false);
|
|
1291
|
+
const [permissionDenied, setPermissionDenied] = (0, import_react6.useState)(false);
|
|
1292
|
+
const { status, publishVideoStream, unpublishVideoStream, reactor } = useReactor((state) => ({
|
|
1293
|
+
status: state.status,
|
|
1294
|
+
publishVideoStream: state.publishVideoStream,
|
|
1295
|
+
unpublishVideoStream: state.unpublishVideoStream,
|
|
1296
|
+
reactor: state.internal.reactor
|
|
1297
|
+
}));
|
|
1298
|
+
const videoRef = (0, import_react6.useRef)(null);
|
|
1299
|
+
const startWebcam = () => __async(null, null, function* () {
|
|
1300
|
+
console.debug("[WebcamPublisher] Starting webcam");
|
|
1301
|
+
try {
|
|
1302
|
+
const mediaStream = yield navigator.mediaDevices.getUserMedia({
|
|
1303
|
+
video: videoConstraints,
|
|
1304
|
+
audio: false
|
|
1305
|
+
});
|
|
1306
|
+
console.debug("[WebcamPublisher] Webcam started successfully");
|
|
1307
|
+
setStream(mediaStream);
|
|
1308
|
+
setPermissionDenied(false);
|
|
1309
|
+
} catch (err) {
|
|
1310
|
+
console.error("[WebcamPublisher] Failed to start webcam:", err);
|
|
1311
|
+
if (err instanceof DOMException && (err.name === "NotAllowedError" || err.name === "PermissionDeniedError")) {
|
|
1312
|
+
console.debug("[WebcamPublisher] Camera permission denied");
|
|
1313
|
+
setPermissionDenied(true);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
const stopWebcam = () => __async(null, null, function* () {
|
|
1318
|
+
console.debug("[WebcamPublisher] Stopping webcam");
|
|
1319
|
+
try {
|
|
1320
|
+
yield unpublishVideoStream();
|
|
1321
|
+
console.debug("[WebcamPublisher] Unpublished before stopping");
|
|
1322
|
+
} catch (err) {
|
|
1323
|
+
console.error("[WebcamPublisher] Error unpublishing before stop:", err);
|
|
1324
|
+
}
|
|
1325
|
+
setIsPublishing(false);
|
|
1326
|
+
stream == null ? void 0 : stream.getTracks().forEach((track) => {
|
|
1327
|
+
track.stop();
|
|
1328
|
+
console.debug("[WebcamPublisher] Stopped track:", track.kind);
|
|
1329
|
+
});
|
|
1330
|
+
setStream(null);
|
|
1331
|
+
console.debug("[WebcamPublisher] Webcam stopped");
|
|
1332
|
+
});
|
|
1333
|
+
(0, import_react6.useEffect)(() => {
|
|
1334
|
+
console.debug("[WebcamPublisher] Stream effect triggered", {
|
|
1335
|
+
hasVideoElement: !!videoRef.current,
|
|
1336
|
+
hasStream: !!stream
|
|
1337
|
+
});
|
|
1338
|
+
if (!videoRef.current) {
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
if (stream) {
|
|
1342
|
+
console.debug("[WebcamPublisher] Attaching stream to video element");
|
|
1343
|
+
videoRef.current.srcObject = stream;
|
|
1344
|
+
console.debug("[WebcamPublisher] Stream attached successfully");
|
|
1345
|
+
} else {
|
|
1346
|
+
console.debug("[WebcamPublisher] Clearing video element");
|
|
1347
|
+
videoRef.current.srcObject = null;
|
|
1348
|
+
}
|
|
1349
|
+
}, [stream]);
|
|
1350
|
+
(0, import_react6.useEffect)(() => {
|
|
1351
|
+
if (!stream) {
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
if (status === "ready" && !isPublishing) {
|
|
1355
|
+
console.debug(
|
|
1356
|
+
"[WebcamPublisher] Reactor ready, auto-publishing webcam stream"
|
|
1357
|
+
);
|
|
1358
|
+
publishVideoStream(stream).then(() => {
|
|
1359
|
+
console.debug("[WebcamPublisher] Auto-publish successful");
|
|
1360
|
+
setIsPublishing(true);
|
|
1361
|
+
}).catch((err) => {
|
|
1362
|
+
console.error("[WebcamPublisher] Auto-publish failed:", err);
|
|
1363
|
+
});
|
|
1364
|
+
} else if (status !== "ready" && isPublishing) {
|
|
1365
|
+
console.debug("[WebcamPublisher] Reactor not ready, auto-unpublishing");
|
|
1366
|
+
unpublishVideoStream().then(() => {
|
|
1367
|
+
console.debug("[WebcamPublisher] Auto-unpublish successful");
|
|
1368
|
+
setIsPublishing(false);
|
|
1369
|
+
}).catch((err) => {
|
|
1370
|
+
console.error("[WebcamPublisher] Auto-unpublish failed:", err);
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
}, [status, stream, isPublishing, publishVideoStream, unpublishVideoStream]);
|
|
1374
|
+
(0, import_react6.useEffect)(() => {
|
|
1375
|
+
const handleError = (error) => {
|
|
1376
|
+
console.debug("[WebcamPublisher] Received error event:", error);
|
|
1377
|
+
if (error.code === "VIDEO_PUBLISH_FAILED") {
|
|
1378
|
+
console.debug(
|
|
1379
|
+
"[WebcamPublisher] Video publish failed, resetting isPublishing state"
|
|
1380
|
+
);
|
|
1381
|
+
setIsPublishing(false);
|
|
1382
|
+
}
|
|
1383
|
+
};
|
|
1384
|
+
reactor.on("error", handleError);
|
|
1385
|
+
return () => {
|
|
1386
|
+
reactor.off("error", handleError);
|
|
1387
|
+
};
|
|
1388
|
+
}, [reactor]);
|
|
1389
|
+
(0, import_react6.useEffect)(() => {
|
|
1390
|
+
if (status !== "ready") {
|
|
1391
|
+
console.debug(
|
|
1392
|
+
"[WebcamPublisher] Status changed to",
|
|
1393
|
+
status,
|
|
1394
|
+
"- resetting isPublishing state"
|
|
1395
|
+
);
|
|
1396
|
+
setIsPublishing(false);
|
|
1397
|
+
}
|
|
1398
|
+
}, [status, isPublishing]);
|
|
1399
|
+
(0, import_react6.useEffect)(() => {
|
|
1400
|
+
console.debug("[WebcamPublisher] Auto-starting webcam");
|
|
1401
|
+
startWebcam();
|
|
1402
|
+
return () => {
|
|
1403
|
+
console.debug("[WebcamPublisher] Cleanup on unmount");
|
|
1404
|
+
stopWebcam();
|
|
1405
|
+
};
|
|
1406
|
+
}, []);
|
|
1407
|
+
const showPlaceholder = !stream;
|
|
1408
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1409
|
+
"div",
|
|
1410
|
+
{
|
|
1411
|
+
style: __spreadValues({
|
|
1412
|
+
display: showWebcam ? "block" : "none",
|
|
1413
|
+
position: "relative",
|
|
1414
|
+
background: "#000"
|
|
1415
|
+
}, style),
|
|
1416
|
+
className,
|
|
1417
|
+
children: [
|
|
1418
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1419
|
+
"video",
|
|
1420
|
+
{
|
|
1421
|
+
ref: videoRef,
|
|
1422
|
+
style: {
|
|
1423
|
+
width: "100%",
|
|
1424
|
+
height: "100%",
|
|
1425
|
+
objectFit: videoObjectFit,
|
|
1426
|
+
display: showPlaceholder ? "none" : "block"
|
|
1427
|
+
},
|
|
1428
|
+
muted: true,
|
|
1429
|
+
playsInline: true,
|
|
1430
|
+
autoPlay: true
|
|
1431
|
+
}
|
|
1432
|
+
),
|
|
1433
|
+
showPlaceholder && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1434
|
+
"div",
|
|
1435
|
+
{
|
|
1436
|
+
style: {
|
|
1437
|
+
position: "absolute",
|
|
1438
|
+
top: 0,
|
|
1439
|
+
left: 0,
|
|
1440
|
+
width: "100%",
|
|
1441
|
+
height: "100%",
|
|
1442
|
+
color: "#fff",
|
|
1443
|
+
display: "flex",
|
|
1444
|
+
alignItems: "center",
|
|
1445
|
+
justifyContent: "center",
|
|
1446
|
+
fontSize: "16px",
|
|
1447
|
+
fontFamily: "monospace",
|
|
1448
|
+
textAlign: "center",
|
|
1449
|
+
padding: "20px",
|
|
1450
|
+
boxSizing: "border-box",
|
|
1451
|
+
flexDirection: "column",
|
|
1452
|
+
gap: "12px"
|
|
1453
|
+
},
|
|
1454
|
+
children: permissionDenied ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: "12px", fontFamily: "monospace" }, children: [
|
|
1455
|
+
"Camera access denied.",
|
|
1456
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("br", {}),
|
|
1457
|
+
"Please allow access in your browser settings."
|
|
1458
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontSize: "12px", fontFamily: "monospace" }, children: "Starting camera..." })
|
|
1459
|
+
}
|
|
1460
|
+
)
|
|
1461
|
+
]
|
|
1462
|
+
}
|
|
1463
|
+
);
|
|
1464
|
+
}
|
|
1181
1465
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1182
1466
|
0 && (module.exports = {
|
|
1183
1467
|
Reactor,
|
|
1184
1468
|
ReactorProvider,
|
|
1185
1469
|
ReactorView,
|
|
1470
|
+
WebcamStream,
|
|
1186
1471
|
useReactor,
|
|
1187
1472
|
useReactorMessage,
|
|
1188
1473
|
useReactorStore
|