@reactor-team/js-sdk 1.0.15 → 1.0.17

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.mjs CHANGED
@@ -90,11 +90,19 @@ var WaitingInfoMessageSchema = z.object({
90
90
  type: z.literal("waiting-info"),
91
91
  data: WaitingInfoDataSchema
92
92
  });
93
+ var SessionExpirationDataSchema = z.object({
94
+ expire: z.number()
95
+ });
96
+ var SessionExpirationMessageSchema = z.object({
97
+ type: z.literal("session-expiration"),
98
+ data: SessionExpirationDataSchema
99
+ });
93
100
  var CoordinatorMessageSchema = z.discriminatedUnion("type", [
94
101
  WelcomeMessageSchema,
95
102
  GPUMachineAssignmentMessageSchema,
96
103
  EchoMessageSchema,
97
- WaitingInfoMessageSchema
104
+ WaitingInfoMessageSchema,
105
+ SessionExpirationMessageSchema
98
106
  ]);
99
107
  var GPUMachineSendMessageSchema = z.discriminatedUnion("type", [
100
108
  ApplicationMessageSchema
@@ -761,6 +769,12 @@ var Reactor = class {
761
769
  this.setWaitingInfo(__spreadValues(__spreadValues({}, this.waitingInfo), waitingData));
762
770
  }
763
771
  );
772
+ this.coordinatorClient.on(
773
+ "session-expiration",
774
+ (sessionExpirationData) => {
775
+ this.setSessionExpiration(sessionExpirationData.expire);
776
+ }
777
+ );
764
778
  this.coordinatorClient.on(
765
779
  "statusChanged",
766
780
  (newStatus) => {
@@ -826,7 +840,8 @@ var Reactor = class {
826
840
  this.machineClient = void 0;
827
841
  }
828
842
  this.setStatus("disconnected");
829
- this.waitingInfo = void 0;
843
+ this.setSessionExpiration(void 0);
844
+ this.setWaitingInfo(void 0);
830
845
  });
831
846
  }
832
847
  setStatus(newStatus) {
@@ -848,6 +863,20 @@ var Reactor = class {
848
863
  this.emit("waitingInfoChanged", newWaitingInfo);
849
864
  }
850
865
  }
866
+ /**
867
+ * Set the session expiration time.
868
+ * @param newSessionExpiration The new session expiration time in seconds.
869
+ */
870
+ setSessionExpiration(newSessionExpiration) {
871
+ console.debug(
872
+ "[Reactor] Setting session expiration:",
873
+ newSessionExpiration
874
+ );
875
+ if (this.sessionExpiration !== newSessionExpiration) {
876
+ this.sessionExpiration = newSessionExpiration;
877
+ this.emit("sessionExpirationChanged", newSessionExpiration);
878
+ }
879
+ }
851
880
  getStatus() {
852
881
  return this.status;
853
882
  }
@@ -903,7 +932,8 @@ var defaultInitState = {
903
932
  videoTrack: null,
904
933
  fps: void 0,
905
934
  waitingInfo: void 0,
906
- lastError: void 0
935
+ lastError: void 0,
936
+ sessionExpiration: void 0
907
937
  };
908
938
  var initReactorStore = (props) => {
909
939
  return __spreadValues(__spreadValues({}, defaultInitState), props);
@@ -930,6 +960,13 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
930
960
  });
931
961
  set({ waitingInfo: newWaitingInfo });
932
962
  });
963
+ reactor.on("sessionExpirationChanged", (newSessionExpiration) => {
964
+ console.debug("[ReactorStore] Session expiration changed", {
965
+ oldSessionExpiration: get().sessionExpiration,
966
+ newSessionExpiration
967
+ });
968
+ set({ sessionExpiration: newSessionExpiration });
969
+ });
933
970
  reactor.on("streamChanged", (videoTrack) => {
934
971
  console.debug("[ReactorStore] Stream changed", {
935
972
  hasVideoTrack: !!videoTrack,
@@ -1259,9 +1296,454 @@ function ReactorView({
1259
1296
  );
1260
1297
  }
1261
1298
 
1262
- // src/react/WebcamStream.tsx
1263
- import { useEffect as useEffect4, useRef as useRef4, useState as useState2 } from "react";
1299
+ // src/react/ReactorController.tsx
1300
+ import React, { useState as useState2, useCallback } from "react";
1264
1301
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1302
+ function ReactorController({
1303
+ className,
1304
+ style
1305
+ }) {
1306
+ const { sendMessage, status } = useReactor((state) => ({
1307
+ sendMessage: state.sendMessage,
1308
+ status: state.status
1309
+ }));
1310
+ const [commands, setCommands] = useState2({});
1311
+ const [formValues, setFormValues] = useState2({});
1312
+ const [expandedCommands, setExpandedCommands] = useState2({});
1313
+ React.useEffect(() => {
1314
+ if (status === "disconnected") {
1315
+ setCommands({});
1316
+ setFormValues({});
1317
+ setExpandedCommands({});
1318
+ }
1319
+ }, [status]);
1320
+ React.useEffect(() => {
1321
+ if (status === "ready") {
1322
+ sendMessage({
1323
+ type: "requestCapabilities",
1324
+ data: {}
1325
+ });
1326
+ }
1327
+ }, [status, sendMessage]);
1328
+ useReactorMessage((message) => {
1329
+ if (message && typeof message === "object" && "commands" in message) {
1330
+ const commandsMessage = message;
1331
+ setCommands(commandsMessage.commands);
1332
+ const initialValues = {};
1333
+ const initialExpanded = {};
1334
+ Object.entries(commandsMessage.commands).forEach(
1335
+ ([commandName, commandSchema]) => {
1336
+ initialValues[commandName] = {};
1337
+ initialExpanded[commandName] = false;
1338
+ Object.entries(commandSchema.schema).forEach(
1339
+ ([paramName, paramSchema]) => {
1340
+ var _a, _b;
1341
+ if (paramSchema.type === "number") {
1342
+ initialValues[commandName][paramName] = (_a = paramSchema.minimum) != null ? _a : 0;
1343
+ } else if (paramSchema.type === "string") {
1344
+ initialValues[commandName][paramName] = "";
1345
+ } else if (paramSchema.type === "boolean") {
1346
+ initialValues[commandName][paramName] = false;
1347
+ } else if (paramSchema.type === "integer") {
1348
+ initialValues[commandName][paramName] = (_b = paramSchema.minimum) != null ? _b : 0;
1349
+ }
1350
+ }
1351
+ );
1352
+ }
1353
+ );
1354
+ setFormValues(initialValues);
1355
+ setExpandedCommands(initialExpanded);
1356
+ }
1357
+ });
1358
+ const handleInputChange = useCallback(
1359
+ (commandName, paramName, value) => {
1360
+ setFormValues((prev) => __spreadProps(__spreadValues({}, prev), {
1361
+ [commandName]: __spreadProps(__spreadValues({}, prev[commandName]), {
1362
+ [paramName]: value
1363
+ })
1364
+ }));
1365
+ },
1366
+ []
1367
+ );
1368
+ const toggleCommandExpanded = useCallback((commandName) => {
1369
+ setExpandedCommands((prev) => __spreadProps(__spreadValues({}, prev), {
1370
+ [commandName]: !prev[commandName]
1371
+ }));
1372
+ }, []);
1373
+ const handleCommandSubmit = useCallback(
1374
+ (commandName) => __async(null, null, function* () {
1375
+ const commandSchema = commands[commandName];
1376
+ const formData = formValues[commandName] || {};
1377
+ const data = {};
1378
+ Object.keys(commandSchema.schema).forEach((paramName) => {
1379
+ var _a, _b;
1380
+ const paramSchema = commandSchema.schema[paramName];
1381
+ let value = formData[paramName];
1382
+ if (paramSchema.type === "number" && typeof value === "string") {
1383
+ value = parseFloat(value) || 0;
1384
+ } else if (paramSchema.type === "integer" && typeof value === "string") {
1385
+ value = parseInt(value) || 0;
1386
+ } else if (paramSchema.type === "boolean" && typeof value !== "boolean") {
1387
+ value = Boolean(value);
1388
+ }
1389
+ if (value !== void 0 && value !== "" && value !== null) {
1390
+ data[paramName] = value;
1391
+ } else if (paramSchema.required) {
1392
+ if (paramSchema.type === "number") {
1393
+ data[paramName] = (_a = paramSchema.minimum) != null ? _a : 0;
1394
+ } else if (paramSchema.type === "integer") {
1395
+ data[paramName] = (_b = paramSchema.minimum) != null ? _b : 0;
1396
+ } else if (paramSchema.type === "string") {
1397
+ data[paramName] = "";
1398
+ } else if (paramSchema.type === "boolean") {
1399
+ data[paramName] = false;
1400
+ }
1401
+ }
1402
+ });
1403
+ console.log(`Executing command: ${commandName}`, data);
1404
+ yield sendMessage({
1405
+ type: commandName,
1406
+ data
1407
+ });
1408
+ }),
1409
+ [formValues, sendMessage, commands]
1410
+ );
1411
+ const renderInput = (commandName, paramName, paramSchema) => {
1412
+ var _a, _b;
1413
+ const value = (_b = (_a = formValues[commandName]) == null ? void 0 : _a[paramName]) != null ? _b : "";
1414
+ if (paramSchema.type === "number" || paramSchema.type === "integer") {
1415
+ const isInteger = paramSchema.type === "integer";
1416
+ const step = isInteger ? 1 : 0.1;
1417
+ const parseValue = isInteger ? parseInt : parseFloat;
1418
+ if (typeof paramSchema.minimum === "number" && typeof paramSchema.maximum === "number") {
1419
+ return /* @__PURE__ */ jsxs2("div", { style: { marginBottom: "8px" }, children: [
1420
+ /* @__PURE__ */ jsxs2(
1421
+ "label",
1422
+ {
1423
+ style: { fontSize: "12px", color: "#666", display: "block" },
1424
+ children: [
1425
+ paramName,
1426
+ " (",
1427
+ paramSchema.minimum,
1428
+ " - ",
1429
+ paramSchema.maximum,
1430
+ ")",
1431
+ paramSchema.description && ` - ${paramSchema.description}`,
1432
+ paramSchema.required && /* @__PURE__ */ jsx3("span", { style: { color: "red" }, children: " *" })
1433
+ ]
1434
+ }
1435
+ ),
1436
+ /* @__PURE__ */ jsx3(
1437
+ "input",
1438
+ {
1439
+ type: "range",
1440
+ min: paramSchema.minimum,
1441
+ max: paramSchema.maximum,
1442
+ step,
1443
+ value,
1444
+ onChange: (e) => {
1445
+ const newValue = parseValue(e.target.value) || 0;
1446
+ handleInputChange(commandName, paramName, newValue);
1447
+ handleCommandSubmit(commandName);
1448
+ },
1449
+ style: { width: "100%", marginBottom: "4px" }
1450
+ }
1451
+ ),
1452
+ /* @__PURE__ */ jsxs2("div", { style: { fontSize: "11px", color: "#888" }, children: [
1453
+ "Value: ",
1454
+ value
1455
+ ] })
1456
+ ] });
1457
+ } else {
1458
+ return /* @__PURE__ */ jsxs2("div", { style: { marginBottom: "8px" }, children: [
1459
+ /* @__PURE__ */ jsxs2(
1460
+ "label",
1461
+ {
1462
+ style: { fontSize: "12px", color: "#666", display: "block" },
1463
+ children: [
1464
+ paramName,
1465
+ paramSchema.description && ` - ${paramSchema.description}`,
1466
+ paramSchema.required && /* @__PURE__ */ jsx3("span", { style: { color: "red" }, children: " *" })
1467
+ ]
1468
+ }
1469
+ ),
1470
+ /* @__PURE__ */ jsx3(
1471
+ "input",
1472
+ {
1473
+ type: "number",
1474
+ value,
1475
+ min: paramSchema.minimum,
1476
+ max: paramSchema.maximum,
1477
+ step,
1478
+ inputMode: "numeric",
1479
+ onChange: (e) => {
1480
+ const val = e.target.value;
1481
+ if (val === "" || val === "-") {
1482
+ handleInputChange(commandName, paramName, val);
1483
+ } else {
1484
+ const parsed = parseValue(val);
1485
+ if (!isNaN(parsed)) {
1486
+ handleInputChange(commandName, paramName, parsed);
1487
+ }
1488
+ }
1489
+ },
1490
+ onBlur: (e) => {
1491
+ const val = e.target.value;
1492
+ if (val === "" || val === "-") {
1493
+ handleInputChange(commandName, paramName, 0);
1494
+ }
1495
+ },
1496
+ style: {
1497
+ width: "100%",
1498
+ padding: "4px",
1499
+ fontSize: "12px",
1500
+ border: "1px solid #ccc",
1501
+ borderRadius: "2px"
1502
+ }
1503
+ }
1504
+ )
1505
+ ] });
1506
+ }
1507
+ } else if (paramSchema.type === "string") {
1508
+ if (paramSchema.enum) {
1509
+ return /* @__PURE__ */ jsxs2("div", { style: { marginBottom: "8px" }, children: [
1510
+ /* @__PURE__ */ jsxs2(
1511
+ "label",
1512
+ {
1513
+ style: { fontSize: "12px", color: "#666", display: "block" },
1514
+ children: [
1515
+ paramName,
1516
+ paramSchema.description && ` - ${paramSchema.description}`,
1517
+ paramSchema.required && /* @__PURE__ */ jsx3("span", { style: { color: "red" }, children: " *" })
1518
+ ]
1519
+ }
1520
+ ),
1521
+ /* @__PURE__ */ jsxs2(
1522
+ "select",
1523
+ {
1524
+ value,
1525
+ onChange: (e) => handleInputChange(commandName, paramName, e.target.value),
1526
+ style: {
1527
+ width: "100%",
1528
+ padding: "4px",
1529
+ fontSize: "12px",
1530
+ border: "1px solid #ccc",
1531
+ borderRadius: "2px"
1532
+ },
1533
+ children: [
1534
+ /* @__PURE__ */ jsx3("option", { value: "", children: "Select..." }),
1535
+ paramSchema.enum.map((option) => /* @__PURE__ */ jsx3("option", { value: option, children: option }, option))
1536
+ ]
1537
+ }
1538
+ )
1539
+ ] });
1540
+ } else {
1541
+ return /* @__PURE__ */ jsxs2("div", { style: { marginBottom: "8px" }, children: [
1542
+ /* @__PURE__ */ jsxs2(
1543
+ "label",
1544
+ {
1545
+ style: { fontSize: "12px", color: "#666", display: "block" },
1546
+ children: [
1547
+ paramName,
1548
+ paramSchema.description && ` - ${paramSchema.description}`,
1549
+ paramSchema.required && /* @__PURE__ */ jsx3("span", { style: { color: "red" }, children: " *" })
1550
+ ]
1551
+ }
1552
+ ),
1553
+ /* @__PURE__ */ jsx3(
1554
+ "input",
1555
+ {
1556
+ type: "text",
1557
+ value,
1558
+ onChange: (e) => handleInputChange(commandName, paramName, e.target.value),
1559
+ style: {
1560
+ width: "100%",
1561
+ padding: "4px",
1562
+ fontSize: "12px",
1563
+ border: "1px solid #ccc",
1564
+ borderRadius: "2px"
1565
+ }
1566
+ }
1567
+ )
1568
+ ] });
1569
+ }
1570
+ } else if (paramSchema.type === "boolean") {
1571
+ return /* @__PURE__ */ jsx3("div", { style: { marginBottom: "8px" }, children: /* @__PURE__ */ jsxs2(
1572
+ "label",
1573
+ {
1574
+ style: {
1575
+ fontSize: "12px",
1576
+ color: "#666",
1577
+ display: "flex",
1578
+ alignItems: "center"
1579
+ },
1580
+ children: [
1581
+ /* @__PURE__ */ jsx3(
1582
+ "input",
1583
+ {
1584
+ type: "checkbox",
1585
+ checked: value,
1586
+ onChange: (e) => handleInputChange(commandName, paramName, e.target.checked),
1587
+ style: { marginRight: "6px" }
1588
+ }
1589
+ ),
1590
+ paramName,
1591
+ paramSchema.description && ` - ${paramSchema.description}`,
1592
+ paramSchema.required && /* @__PURE__ */ jsx3("span", { style: { color: "red" }, children: " *" })
1593
+ ]
1594
+ }
1595
+ ) });
1596
+ }
1597
+ return null;
1598
+ };
1599
+ const renderCommand = (commandName, commandSchema) => {
1600
+ const hasParams = Object.keys(commandSchema.schema).length > 0;
1601
+ const isExpanded = expandedCommands[commandName];
1602
+ const hasSliderInputs = Object.values(commandSchema.schema).some(
1603
+ (paramSchema) => (paramSchema.type === "number" || paramSchema.type === "integer") && typeof paramSchema.minimum === "number" && typeof paramSchema.maximum === "number"
1604
+ );
1605
+ const showExecuteButton = !hasSliderInputs;
1606
+ return /* @__PURE__ */ jsxs2(
1607
+ "div",
1608
+ {
1609
+ style: {
1610
+ border: "1px solid #ddd",
1611
+ borderRadius: "4px",
1612
+ marginBottom: "8px",
1613
+ backgroundColor: "#fafafa"
1614
+ },
1615
+ children: [
1616
+ /* @__PURE__ */ jsxs2(
1617
+ "div",
1618
+ {
1619
+ onClick: () => toggleCommandExpanded(commandName),
1620
+ style: {
1621
+ padding: "8px 12px",
1622
+ cursor: "pointer",
1623
+ borderBottom: isExpanded ? "1px solid #ddd" : "none",
1624
+ display: "flex",
1625
+ justifyContent: "space-between",
1626
+ alignItems: "center"
1627
+ },
1628
+ children: [
1629
+ /* @__PURE__ */ jsxs2("div", { children: [
1630
+ /* @__PURE__ */ jsx3(
1631
+ "h4",
1632
+ {
1633
+ style: {
1634
+ margin: "0",
1635
+ fontSize: "13px",
1636
+ fontWeight: "bold"
1637
+ },
1638
+ children: commandName
1639
+ }
1640
+ ),
1641
+ isExpanded && commandSchema.description && /* @__PURE__ */ jsx3(
1642
+ "p",
1643
+ {
1644
+ style: { margin: "4px 0 0 0", fontSize: "11px", color: "#666" },
1645
+ children: commandSchema.description
1646
+ }
1647
+ )
1648
+ ] }),
1649
+ /* @__PURE__ */ jsx3(
1650
+ "div",
1651
+ {
1652
+ style: {
1653
+ fontSize: "10px",
1654
+ color: "#999",
1655
+ transform: isExpanded ? "rotate(180deg)" : "rotate(0deg)",
1656
+ transition: "transform 0.2s"
1657
+ },
1658
+ children: "\u25BC"
1659
+ }
1660
+ )
1661
+ ]
1662
+ }
1663
+ ),
1664
+ isExpanded && /* @__PURE__ */ jsxs2("div", { style: { padding: "12px", paddingTop: "0" }, children: [
1665
+ hasParams && /* @__PURE__ */ jsxs2("div", { style: { marginBottom: showExecuteButton ? "12px" : "0" }, children: [
1666
+ /* @__PURE__ */ jsx3(
1667
+ "div",
1668
+ {
1669
+ style: {
1670
+ marginBottom: "8px",
1671
+ fontSize: "12px",
1672
+ fontWeight: "bold",
1673
+ color: "#555"
1674
+ },
1675
+ children: "Parameters:"
1676
+ }
1677
+ ),
1678
+ Object.entries(commandSchema.schema).map(
1679
+ ([paramName, paramSchema]) => /* @__PURE__ */ jsx3(
1680
+ "div",
1681
+ {
1682
+ style: { marginLeft: "8px" },
1683
+ children: renderInput(commandName, paramName, paramSchema)
1684
+ },
1685
+ `${commandName}-${paramName}`
1686
+ )
1687
+ )
1688
+ ] }),
1689
+ !hasParams && /* @__PURE__ */ jsx3(
1690
+ "div",
1691
+ {
1692
+ style: {
1693
+ marginBottom: showExecuteButton ? "12px" : "0",
1694
+ marginTop: "2px",
1695
+ fontSize: "11px",
1696
+ color: "#666",
1697
+ fontStyle: "italic"
1698
+ },
1699
+ children: "No parameters required"
1700
+ }
1701
+ ),
1702
+ showExecuteButton && /* @__PURE__ */ jsxs2(
1703
+ "button",
1704
+ {
1705
+ onClick: () => handleCommandSubmit(commandName),
1706
+ style: {
1707
+ padding: "8px 16px",
1708
+ fontSize: "12px",
1709
+ backgroundColor: "#007bff",
1710
+ color: "white",
1711
+ border: "none",
1712
+ borderRadius: "4px",
1713
+ cursor: "pointer",
1714
+ fontWeight: "bold"
1715
+ },
1716
+ children: [
1717
+ "Execute ",
1718
+ commandName
1719
+ ]
1720
+ }
1721
+ )
1722
+ ] })
1723
+ ]
1724
+ },
1725
+ commandName
1726
+ );
1727
+ };
1728
+ return /* @__PURE__ */ jsx3("div", { className, style, children: /* @__PURE__ */ jsx3("div", { style: { fontFamily: "monospace", fontSize: "12px" }, children: Object.keys(commands).length === 0 ? /* @__PURE__ */ jsx3("div", { style: { padding: "12px", color: "#666", fontStyle: "italic" }, children: "Waiting for commands schema..." }) : /* @__PURE__ */ jsxs2("div", { children: [
1729
+ /* @__PURE__ */ jsx3(
1730
+ "h3",
1731
+ {
1732
+ style: {
1733
+ margin: "0 0 16px 0",
1734
+ fontSize: "16px",
1735
+ fontWeight: "bold"
1736
+ },
1737
+ children: "Reactor Commands"
1738
+ }
1739
+ ),
1740
+ Object.entries(commands).map(([commandName, commandSchema]) => /* @__PURE__ */ jsx3("div", { children: renderCommand(commandName, commandSchema) }, commandName))
1741
+ ] }) }) });
1742
+ }
1743
+
1744
+ // src/react/WebcamStream.tsx
1745
+ import { useEffect as useEffect4, useRef as useRef4, useState as useState3 } from "react";
1746
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1265
1747
  function WebcamStream({
1266
1748
  className,
1267
1749
  style,
@@ -1272,9 +1754,9 @@ function WebcamStream({
1272
1754
  showWebcam = true,
1273
1755
  videoObjectFit = "contain"
1274
1756
  }) {
1275
- const [stream, setStream] = useState2(null);
1276
- const [isPublishing, setIsPublishing] = useState2(false);
1277
- const [permissionDenied, setPermissionDenied] = useState2(false);
1757
+ const [stream, setStream] = useState3(null);
1758
+ const [isPublishing, setIsPublishing] = useState3(false);
1759
+ const [permissionDenied, setPermissionDenied] = useState3(false);
1278
1760
  const { status, publishVideoStream, unpublishVideoStream, reactor } = useReactor((state) => ({
1279
1761
  status: state.status,
1280
1762
  publishVideoStream: state.publishVideoStream,
@@ -1391,7 +1873,7 @@ function WebcamStream({
1391
1873
  };
1392
1874
  }, []);
1393
1875
  const showPlaceholder = !stream;
1394
- return /* @__PURE__ */ jsxs2(
1876
+ return /* @__PURE__ */ jsxs3(
1395
1877
  "div",
1396
1878
  {
1397
1879
  style: __spreadValues({
@@ -1401,7 +1883,7 @@ function WebcamStream({
1401
1883
  }, style),
1402
1884
  className,
1403
1885
  children: [
1404
- /* @__PURE__ */ jsx3(
1886
+ /* @__PURE__ */ jsx4(
1405
1887
  "video",
1406
1888
  {
1407
1889
  ref: videoRef,
@@ -1416,7 +1898,7 @@ function WebcamStream({
1416
1898
  autoPlay: true
1417
1899
  }
1418
1900
  ),
1419
- showPlaceholder && /* @__PURE__ */ jsx3(
1901
+ showPlaceholder && /* @__PURE__ */ jsx4(
1420
1902
  "div",
1421
1903
  {
1422
1904
  style: {
@@ -1437,11 +1919,11 @@ function WebcamStream({
1437
1919
  flexDirection: "column",
1438
1920
  gap: "12px"
1439
1921
  },
1440
- children: permissionDenied ? /* @__PURE__ */ jsxs2("div", { style: { fontSize: "12px", fontFamily: "monospace" }, children: [
1922
+ children: permissionDenied ? /* @__PURE__ */ jsxs3("div", { style: { fontSize: "12px", fontFamily: "monospace" }, children: [
1441
1923
  "Camera access denied.",
1442
- /* @__PURE__ */ jsx3("br", {}),
1924
+ /* @__PURE__ */ jsx4("br", {}),
1443
1925
  "Please allow access in your browser settings."
1444
- ] }) : /* @__PURE__ */ jsx3("div", { style: { fontSize: "12px", fontFamily: "monospace" }, children: "Starting camera..." })
1926
+ ] }) : /* @__PURE__ */ jsx4("div", { style: { fontSize: "12px", fontFamily: "monospace" }, children: "Starting camera..." })
1445
1927
  }
1446
1928
  )
1447
1929
  ]
@@ -1450,6 +1932,7 @@ function WebcamStream({
1450
1932
  }
1451
1933
  export {
1452
1934
  Reactor,
1935
+ ReactorController,
1453
1936
  ReactorProvider,
1454
1937
  ReactorView,
1455
1938
  WebcamStream,