@limrun/ui 0.13.4 → 0.13.5

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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx as H, jsxs as $, Fragment as _e } from "react/jsx-runtime";
2
- import { memo as Ye, useState as L, useEffect as cA, useMemo as KA, useCallback as Zt, forwardRef as kt, useRef as z, useImperativeHandle as Ot } from "react";
2
+ import { memo as Ye, useState as L, useEffect as sA, useMemo as KA, useCallback as Zt, forwardRef as kt, useRef as z, useImperativeHandle as Ot } from "react";
3
3
  import { createPortal as St } from "react-dom";
4
4
  import './index.css';function $e(t) {
5
5
  var e, s, o = "";
@@ -655,20 +655,20 @@ const on = async (t) => {
655
655
  onTap: w
656
656
  }) {
657
657
  const [d, k] = L(null);
658
- cA(() => {
658
+ sA(() => {
659
659
  if (!d) return;
660
660
  const P = window.setTimeout(() => k(null), 1100);
661
661
  return () => window.clearTimeout(P);
662
662
  }, [d]);
663
663
  const l = KA(() => {
664
- const P = gn, b = an, O = Cn, J = In, uA = typeof window < "u" ? window.innerWidth : 1024, GA = typeof window < "u" ? window.innerHeight : 768, hA = o.x + O + P <= uA - J, EA = o.y - O - b >= J;
665
- let QA = hA ? o.x + O : o.x - O, xA = EA ? o.y - O : o.y + O;
666
- const Ie = `translate(${hA ? "0" : "-100%"}, ${EA ? "-100%" : "0"})`;
667
- return QA = Math.max(J + (hA ? 0 : P), Math.min(uA - J - (hA ? P : 0), QA)), xA = Math.max(J + (EA ? b : 0), Math.min(GA - J - (EA ? 0 : b), xA)), { left: `${QA}px`, top: `${xA}px`, transform: Ie };
668
- }, [o.x, o.y]), F = KA(() => $t(e, s), [e, s]), Z = s === "ios" ? e.selectors.AXUniqueId : e.selectors.resourceId, tA = s === "ios" ? "AXUniqueId" : "resourceId", sA = Zt(async (P, b) => {
664
+ const P = gn, b = an, O = Cn, J = In, uA = typeof window < "u" ? window.innerWidth : 1024, GA = typeof window < "u" ? window.innerHeight : 768, hA = o.x + O + P <= uA - J, gA = o.y - O - b >= J;
665
+ let QA = hA ? o.x + O : o.x - O, xA = gA ? o.y - O : o.y + O;
666
+ const Ie = `translate(${hA ? "0" : "-100%"}, ${gA ? "-100%" : "0"})`;
667
+ return QA = Math.max(J + (hA ? 0 : P), Math.min(uA - J - (hA ? P : 0), QA)), xA = Math.max(J + (gA ? b : 0), Math.min(GA - J - (gA ? 0 : b), xA)), { left: `${QA}px`, top: `${xA}px`, transform: Ie };
668
+ }, [o.x, o.y]), F = KA(() => $t(e, s), [e, s]), Z = s === "ios" ? e.selectors.AXUniqueId : e.selectors.resourceId, tA = s === "ios" ? "AXUniqueId" : "resourceId", oA = Zt(async (P, b) => {
669
669
  if (!P) return;
670
670
  await on(P) && k(b);
671
- }, []), oA = it(e), BA = e.label || e.value || oA, AA = rt(e);
671
+ }, []), EA = it(e), BA = e.label || e.value || EA, AA = rt(e);
672
672
  return St(
673
673
  /* @__PURE__ */ $(
674
674
  "div",
@@ -681,7 +681,7 @@ const on = async (t) => {
681
681
  onMouseMove: (P) => P.stopPropagation(),
682
682
  children: [
683
683
  /* @__PURE__ */ $("div", { className: "rc-inspect-card-header", children: [
684
- /* @__PURE__ */ H("span", { className: "rc-inspect-card-role", children: oA }),
684
+ /* @__PURE__ */ H("span", { className: "rc-inspect-card-role", children: EA }),
685
685
  !e.enabled && /* @__PURE__ */ H("span", { className: "rc-inspect-card-tag", children: "disabled" }),
686
686
  e.focused && /* @__PURE__ */ H("span", { className: "rc-inspect-card-tag rc-inspect-card-tag-blue", children: "focused" })
687
687
  ] }),
@@ -726,7 +726,7 @@ const on = async (t) => {
726
726
  className: fA("rc-inspect-card-btn", d === "selector" && "rc-inspect-card-btn-copied"),
727
727
  disabled: !F,
728
728
  title: F ?? "No usable selector for this element",
729
- onClick: () => sA(F ?? void 0, "selector"),
729
+ onClick: () => oA(F ?? void 0, "selector"),
730
730
  children: d === "selector" ? "Copied!" : "Copy selector"
731
731
  }
732
732
  ),
@@ -737,7 +737,7 @@ const on = async (t) => {
737
737
  className: fA("rc-inspect-card-btn", d === "id" && "rc-inspect-card-btn-copied"),
738
738
  disabled: !Z,
739
739
  title: Z ?? `No ${tA}`,
740
- onClick: () => sA(Z, "id"),
740
+ onClick: () => oA(Z, "id"),
741
741
  children: d === "id" ? "Copied!" : `Copy ${tA}`
742
742
  }
743
743
  )
@@ -758,12 +758,12 @@ const on = async (t) => {
758
758
  onSelectChange: k,
759
759
  onTapElement: l
760
760
  }) {
761
- const F = a === "select", Z = F ? o !== null && o !== I : o !== null, tA = F ? o ?? I : o, sA = KA(() => {
761
+ const F = a === "select", Z = F ? o !== null && o !== I : o !== null, tA = F ? o ?? I : o, oA = KA(() => {
762
762
  const J = /* @__PURE__ */ new Map();
763
763
  if (!e) return J;
764
764
  for (const uA of e.elements) J.set(uA.id, uA);
765
765
  return J;
766
- }, [e]), oA = KA(() => tA ? sA.get(tA) ?? null : null, [tA, sA]);
766
+ }, [e]), EA = KA(() => tA ? oA.get(tA) ?? null : null, [tA, oA]);
767
767
  if (!(!!e && !!s && e.screen.width > 0 && e.screen.height > 0 && e.elements.length > 0)) return null;
768
768
  const AA = Z ? w : d ?? w, P = Z, b = F && !Z && I !== null, O = (J) => {
769
769
  F && J.target === J.currentTarget && k(null, null);
@@ -794,10 +794,10 @@ const on = async (t) => {
794
794
  ))
795
795
  }
796
796
  ),
797
- oA && AA && /* @__PURE__ */ H(
797
+ EA && AA && /* @__PURE__ */ H(
798
798
  Mn,
799
799
  {
800
- element: oA,
800
+ element: EA,
801
801
  platform: e.platform,
802
802
  anchor: AA,
803
803
  cursorAnchored: P,
@@ -873,22 +873,22 @@ const Fn = kt(
873
873
  onAxStatusChange: F,
874
874
  axPollIntervalMs: Z,
875
875
  axMaxBackoffMs: tA,
876
- onCameraDemandChange: sA,
877
- onCameraStats: oA,
876
+ onCameraDemandChange: oA,
877
+ onCameraStats: EA,
878
878
  cameraResolutionCap: BA = "auto",
879
879
  cameraAspect: AA
880
880
  }, P) => {
881
- const b = z(null), O = z(null), J = z(null), [uA, GA] = L(!1), [hA, EA] = L(!1), [QA, xA] = L(!1), [De, Re] = L(!1), [Ie, Fe] = L({}), Q = z(null), y = z(null), j = z(null), zA = z(void 0), yA = z(void 0), JA = z(void 0), qA = z(void 0), YA = z(void 0), DA = z(0), _A = z(0), RA = z(!1), Me = z(w);
881
+ const b = z(null), O = z(null), J = z(null), [uA, GA] = L(!1), [hA, gA] = L(!1), [QA, xA] = L(!1), [De, Re] = L(!1), [Ie, Fe] = L({}), Q = z(null), y = z(null), j = z(null), zA = z(void 0), yA = z(void 0), JA = z(void 0), qA = z(void 0), YA = z(void 0), DA = z(0), _A = z(0), RA = z(!1), Me = z(w);
882
882
  Me.current = w;
883
883
  const HA = z(null), q = z(null), Ze = z(0), ke = z(BA);
884
884
  ke.current = BA;
885
885
  const Oe = z(AA);
886
886
  Oe.current = AA;
887
- const nA = z(sA);
888
- nA.current = sA;
889
- const $A = z(oA);
890
- $A.current = oA;
891
- const PA = z(null), Ae = z(null), NA = z(!1), gA = z(/* @__PURE__ */ new Map()), iA = z(/* @__PURE__ */ new Map()), dA = z(/* @__PURE__ */ new Map()), wA = z(/* @__PURE__ */ new Map()), X = z(/* @__PURE__ */ new Map()), FA = z(!1), [ct, st] = L(!1), U = z(null), [mA, ee] = L(null), T = z(null), [Se, ve] = L(null), [Be, UA] = L(null), [ue, te] = L(null), [ot, pe] = L(null), [Et, je] = L(null), ne = z(null), V = z(void 0), Ke = (A) => {
887
+ const nA = z(oA);
888
+ nA.current = oA;
889
+ const $A = z(EA);
890
+ $A.current = EA;
891
+ const PA = z(null), Ae = z(null), NA = z(!1), aA = z(/* @__PURE__ */ new Map()), iA = z(/* @__PURE__ */ new Map()), dA = z(/* @__PURE__ */ new Map()), wA = z(/* @__PURE__ */ new Map()), X = z(/* @__PURE__ */ new Map()), FA = z(!1), [ct, st] = L(!1), U = z(null), [mA, ee] = L(null), T = z(null), [Se, ve] = L(null), [Be, UA] = L(null), [ue, te] = L(null), [ot, pe] = L(null), [Et, je] = L(null), ne = z(null), V = z(void 0), Ke = (A) => {
892
892
  ne.current = A, V.current === void 0 && (V.current = window.requestAnimationFrame(() => {
893
893
  V.current = void 0, je(ne.current);
894
894
  }));
@@ -905,7 +905,7 @@ const Fn = kt(
905
905
  const TA = d === !0 || d === "hover-only", at = d === "hover-only" ? "hover-only" : "select", rA = KA(
906
906
  () => o || Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15),
907
907
  [o]
908
- ), aA = KA(() => un(e), [e]), eA = fn[aA], v = (A) => {
908
+ ), cA = KA(() => un(e), [e]), eA = fn[cA], v = (A) => {
909
909
  x(A);
910
910
  }, _ = (A) => {
911
911
  !j.current || j.current.readyState !== "open" || (j.current.send(A), T.current?.bumpActivity());
@@ -1018,7 +1018,7 @@ const Fn = kt(
1018
1018
  if (FA.current !== A && (FA.current = A, st(A), A || ee(null), x("[rc-touch][alt] updateAltHeld", {
1019
1019
  nextHeld: A,
1020
1020
  activePointerIds: Array.from(X.current.keys())
1021
- }), aA === "ios" && j.current && j.current.readyState === "open")) {
1021
+ }), cA === "ios" && j.current && j.current.readyState === "open")) {
1022
1022
  const r = A ? c.ACTION_DOWN : c.ACTION_UP, i = bA(
1023
1023
  r,
1024
1024
  c.KEYCODE_ALT_LEFT,
@@ -1094,12 +1094,12 @@ const Fn = kt(
1094
1094
  }), _(B);
1095
1095
  }, pA = (A, r, i, n, C, M, E, B, m) => {
1096
1096
  if (x("[rc-touch2] applyTwoFingerEvent", {
1097
- platform: aA,
1097
+ platform: cA,
1098
1098
  eventType: A,
1099
1099
  video: { width: r, height: i },
1100
1100
  p0: { x: n, y: C, id: B },
1101
1101
  p1: { x: M, y: E, id: m }
1102
- }), aA === "ios") {
1102
+ }), cA === "ios") {
1103
1103
  const h = A === "down" ? G.ACTION_DOWN : A === "move" ? G.ACTION_MOVE : G.ACTION_UP;
1104
1104
  Mt(h, r, i, n, C, M, E);
1105
1105
  } else
@@ -1299,7 +1299,7 @@ const Fn = kt(
1299
1299
  return;
1300
1300
  }
1301
1301
  };
1302
- cA(() => {
1302
+ sA(() => {
1303
1303
  const A = (n) => {
1304
1304
  n.key === "Alt" && we(!0);
1305
1305
  }, r = (n) => {
@@ -1489,7 +1489,7 @@ const Fn = kt(
1489
1489
  return;
1490
1490
  if (RA.current) {
1491
1491
  if (!Me.current) {
1492
- v(`Connection failed after it was established: ${A}`), EA(!0), WA();
1492
+ v(`Connection failed after it was established: ${A}`), gA(!0), WA();
1493
1493
  return;
1494
1494
  }
1495
1495
  v(`Reconnecting after established session dropped: ${A}`), RA.current = !1, _A.current = -1;
@@ -1497,7 +1497,7 @@ const Fn = kt(
1497
1497
  me();
1498
1498
  const i = _A.current + 1;
1499
1499
  if (i >= ze) {
1500
- v(`Connection failed after ${ze} attempts: ${A}`), EA(!0), WA();
1500
+ v(`Connection failed after ${ze} attempts: ${A}`), gA(!0), WA();
1501
1501
  return;
1502
1502
  }
1503
1503
  v(`Retrying connection (${i + 1}/${ze})`), WA(), yA.current = window.setTimeout(() => {
@@ -1505,7 +1505,7 @@ const Fn = kt(
1505
1505
  }, dn);
1506
1506
  }, Xe = async (A = 0) => {
1507
1507
  const r = DA.current + 1;
1508
- DA.current = r, _A.current = A, RA.current = !1, EA(!1), me(), le(), LA(), NA.current = !1, GA(!1), WA();
1508
+ DA.current = r, _A.current = A, RA.current = !1, gA(!1), me(), le(), LA(), NA.current = !1, GA(!1), WA();
1509
1509
  const i = () => r === DA.current;
1510
1510
  JA.current = window.setTimeout(() => {
1511
1511
  JA.current = void 0, !(!i() || RA.current) && VA("Connection did not succeed within 15 seconds", r);
@@ -1560,10 +1560,10 @@ const Fn = kt(
1560
1560
  return;
1561
1561
  const E = new RTCPeerConnection(M);
1562
1562
  y.current = E, E.addTransceiver("audio", { direction: "recvonly" });
1563
- const B = E.addTransceiver("video", { direction: "recvonly" }), m = E.addTransceiver("video", {
1563
+ const B = E.addTransceiver("video", { direction: "recvonly" }), m = cA === "ios" ? E.addTransceiver("video", {
1564
1564
  direction: "sendonly"
1565
- });
1566
- if (HA.current = m.sender, RTCRtpReceiver.getCapabilities) {
1565
+ }) : null;
1566
+ if (HA.current = m?.sender ?? null, RTCRtpReceiver.getCapabilities) {
1567
1567
  const u = RTCRtpReceiver.getCapabilities("video");
1568
1568
  if (u && u.codecs) {
1569
1569
  const f = u.codecs.sort((K, R) => {
@@ -1576,7 +1576,7 @@ const Fn = kt(
1576
1576
  B.setCodecPreferences(f), x("Set codec preferences:", f.map((K) => K.mimeType).join(", "));
1577
1577
  }
1578
1578
  }
1579
- if (RTCRtpSender.getCapabilities) {
1579
+ if (m && RTCRtpSender.getCapabilities) {
1580
1580
  const u = RTCRtpSender.getCapabilities("video");
1581
1581
  if (u && u.codecs) {
1582
1582
  const g = /* @__PURE__ */ new Set(["42e01f", "42e028", "640c1f"]), f = (R) => {
@@ -1602,7 +1602,7 @@ const Fn = kt(
1602
1602
  if (!i() || j.current !== h || Q.current !== n)
1603
1603
  return;
1604
1604
  RA.current = !0, le(), v("Control channel opened"), T.current || (T.current = new sn({
1605
- platform: aA,
1605
+ platform: cA,
1606
1606
  baseIntervalMs: Z,
1607
1607
  maxBackoffMs: tA,
1608
1608
  send: (g) => {
@@ -1742,7 +1742,7 @@ const Fn = kt(
1742
1742
  Y(`Received screenshot error for unknown or handled id: ${g.id}`);
1743
1743
  break;
1744
1744
  }
1745
- Y(`Received screenshot error for id ${g.id}: ${R}`), p(new Error(R)), gA.current.delete(g.id), iA.current.delete(g.id);
1745
+ Y(`Received screenshot error for id ${g.id}: ${R}`), p(new Error(R)), aA.current.delete(g.id), iA.current.delete(g.id);
1746
1746
  break;
1747
1747
  }
1748
1748
  const D = zn(g);
@@ -1750,12 +1750,12 @@ const Fn = kt(
1750
1750
  Y("Received screenshot message without image data:", g);
1751
1751
  break;
1752
1752
  }
1753
- const W = gA.current.get(g.id);
1753
+ const W = aA.current.get(g.id);
1754
1754
  if (!W) {
1755
1755
  Y(`Received screenshot data for unknown or handled id: ${g.id}`);
1756
1756
  break;
1757
1757
  }
1758
- x(`Received screenshot data for id ${g.id}`), W(D), gA.current.delete(g.id), iA.current.delete(g.id);
1758
+ x(`Received screenshot data for id ${g.id}`), W(D), aA.current.delete(g.id), iA.current.delete(g.id);
1759
1759
  break;
1760
1760
  }
1761
1761
  case "screenshotError":
@@ -1768,7 +1768,7 @@ const Fn = kt(
1768
1768
  Y(`Received screenshot error for unknown or handled id: ${g.id}`);
1769
1769
  break;
1770
1770
  }
1771
- Y(`Received screenshot error for id ${g.id}: ${g.message}`), f(new Error(g.message)), gA.current.delete(g.id), iA.current.delete(g.id);
1771
+ Y(`Received screenshot error for id ${g.id}: ${g.message}`), f(new Error(g.message)), aA.current.delete(g.id), iA.current.delete(g.id);
1772
1772
  break;
1773
1773
  case "cameraRequest": {
1774
1774
  const R = g.active === !0, D = ++Ze.current, W = () => i() && Q.current === n && D === Ze.current;
@@ -1930,7 +1930,7 @@ const Fn = kt(
1930
1930
  }, zt = (A) => {
1931
1931
  A.stopPropagation(), he();
1932
1932
  };
1933
- cA(() => {
1933
+ sA(() => {
1934
1934
  const A = q.current;
1935
1935
  if (!A) return;
1936
1936
  const r = A.getVideoTracks()[0];
@@ -1939,7 +1939,7 @@ const Fn = kt(
1939
1939
  r.applyConstraints(i).catch((n) => {
1940
1940
  Y("applyConstraints for new camera cap failed:", n);
1941
1941
  });
1942
- }, [BA]), cA(() => {
1942
+ }, [BA]), sA(() => {
1943
1943
  if (!AA) return;
1944
1944
  const A = Q.current;
1945
1945
  if (!(!A || A.readyState !== WebSocket.OPEN))
@@ -1948,7 +1948,7 @@ const Fn = kt(
1948
1948
  } catch (r) {
1949
1949
  Y("cameraAspect send failed:", r);
1950
1950
  }
1951
- }, [AA]), cA(() => (GA(!1), he(), document.hidden || Pe(), document.addEventListener("visibilitychange", Te), () => {
1951
+ }, [AA]), sA(() => (GA(!1), he(), document.hidden || Pe(), document.addEventListener("visibilitychange", Te), () => {
1952
1952
  Ne(), Qt(), document.removeEventListener("visibilitychange", Te);
1953
1953
  }), [e, s, o]);
1954
1954
  const Le = () => {
@@ -1967,7 +1967,7 @@ const Fn = kt(
1967
1967
  (i) => i && i.left === r.left && i.top === r.top && i.width === r.width && i.height === r.height ? i : r
1968
1968
  );
1969
1969
  };
1970
- cA(() => {
1970
+ sA(() => {
1971
1971
  const A = O.current, r = J.current, i = b.current;
1972
1972
  if (!A) return;
1973
1973
  const n = () => {
@@ -1979,7 +1979,7 @@ const Fn = kt(
1979
1979
  if (E === 0 || B === 0) return;
1980
1980
  const m = A.videoWidth > A.videoHeight;
1981
1981
  xA(m), Re(
1982
- aA === "android" && ln(A.videoWidth, A.videoHeight)
1982
+ cA === "android" && ln(A.videoWidth, A.videoHeight)
1983
1983
  );
1984
1984
  const h = m ? eA.videoPosition.landscape : eA.videoPosition.portrait;
1985
1985
  let u = {};
@@ -1992,12 +1992,12 @@ const Fn = kt(
1992
1992
  return A.addEventListener("resize", M), n(), () => {
1993
1993
  C.disconnect(), A.removeEventListener("loadedmetadata", n), A.removeEventListener("resize", n), A.removeEventListener("resize", M), r && r.removeEventListener("load", n);
1994
1994
  };
1995
- }, [eA, a]), cA(() => {
1995
+ }, [eA, a]), sA(() => {
1996
1996
  const A = T.current;
1997
1997
  TA ? A?.start() : (A?.stop(), ve(null), UA(null), te(null), je(null), ZA(null), ne.current = null, V.current !== void 0 && (window.cancelAnimationFrame(V.current), V.current = void 0), N("onAxSnapshotChange", de.current, null));
1998
- }, [TA]), cA(() => () => {
1998
+ }, [TA]), sA(() => () => {
1999
1999
  V.current !== void 0 && (window.cancelAnimationFrame(V.current), V.current = void 0);
2000
- }, []), cA(() => {
2000
+ }, []), sA(() => {
2001
2001
  if (!TA) return;
2002
2002
  const A = (r) => {
2003
2003
  r.key === "Escape" && (ue || Be) && (te(null), UA(null), ZA(null), N("onInspectSelectionChange", SA.current, null));
@@ -2066,15 +2066,15 @@ const Fn = kt(
2066
2066
  // Matches the type expected by instance API
2067
2067
  id: i
2068
2068
  };
2069
- gA.current.set(i, A), iA.current.set(i, r), x("Sending screenshot request:", n);
2069
+ aA.current.set(i, A), iA.current.set(i, r), x("Sending screenshot request:", n);
2070
2070
  try {
2071
2071
  Q.current.send(JSON.stringify(n));
2072
2072
  } catch (C) {
2073
- Y("Failed to send screenshot request immediately:", C), gA.current.delete(i), iA.current.delete(i), r(C);
2073
+ Y("Failed to send screenshot request immediately:", C), aA.current.delete(i), iA.current.delete(i), r(C);
2074
2074
  return;
2075
2075
  }
2076
2076
  setTimeout(() => {
2077
- gA.current.has(i) && (Y(`Screenshot request timed out for id ${i}`), iA.current.get(i)?.(new Error("Screenshot request timed out")), gA.current.delete(i), iA.current.delete(i));
2077
+ aA.current.has(i) && (Y(`Screenshot request timed out for id ${i}`), iA.current.get(i)?.(new Error("Screenshot request timed out")), aA.current.delete(i), iA.current.delete(i));
2078
2078
  }, 3e4);
2079
2079
  }),
2080
2080
  terminateApp: (A) => new Promise((r, i) => {
@@ -2114,7 +2114,7 @@ const Fn = kt(
2114
2114
  },
2115
2115
  getAxStatus: () => T.current?.getStatus() ?? "idle"
2116
2116
  }));
2117
- const Dt = ct && mA !== null, Rt = aA === "android" && De ? QA ? Gt : Kt : QA ? eA.frame.imageLandscape : eA.frame.image;
2117
+ const Dt = ct && mA !== null, Rt = cA === "android" && De ? QA ? Gt : Kt : QA ? eA.frame.imageLandscape : eA.frame.image;
2118
2118
  return /* @__PURE__ */ $(
2119
2119
  "div",
2120
2120
  {
@@ -2158,7 +2158,7 @@ const Fn = kt(
2158
2158
  ref: J,
2159
2159
  src: Rt,
2160
2160
  alt: "",
2161
- className: aA === "ios" ? fA("rc-phone-frame", "rc-phone-frame-ios") : "rc-phone-frame",
2161
+ className: cA === "ios" ? fA("rc-phone-frame", "rc-phone-frame-ios") : "rc-phone-frame",
2162
2162
  draggable: !1
2163
2163
  }
2164
2164
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@limrun/ui",
3
- "version": "0.13.4",
3
+ "version": "0.13.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -2035,18 +2035,22 @@ export const RemoteControl = forwardRef<RemoteControlHandle, RemoteControlProps>
2035
2035
  peerConnection.addTransceiver('audio', { direction: 'recvonly' });
2036
2036
  const videoTransceiver = peerConnection.addTransceiver('video', { direction: 'recvonly' });
2037
2037
 
2038
- // Pre-allocate a sendonly video slot for the user's camera.
2039
- // The track stays `null` until the limulator side asks for one
2040
- // (via the `cameraRequest` WS message), at which point we call
2041
- // `replaceTrack` with a `getUserMedia` result. Allocating
2042
- // up-front means we don't have to renegotiate the SDP when the
2043
- // simulator app actually opens its `AVCaptureSession` — the
2044
- // codec/SSRC negotiation happens once, here, and turning the
2045
- // camera on/off later is just a track-replace.
2046
- const outboundCameraTransceiver = peerConnection.addTransceiver('video', {
2047
- direction: 'sendonly',
2048
- });
2049
- outboundCameraSenderRef.current = outboundCameraTransceiver.sender;
2038
+ // Pre-allocate a sendonly slot for the user's camera so the
2039
+ // sim can pull frames later without renegotiating SDP.
2040
+ // Allocating up-front means we don't have to renegotiate the
2041
+ // SDP when the simulator app actually opens its
2042
+ // `AVCaptureSession` — the codec/SSRC negotiation happens
2043
+ // once, here, and turning the camera on/off later is just a
2044
+ // `replaceTrack`.
2045
+ // iOS only: Android has no camera consumer, and the extra
2046
+ // m=video line crashes Android 14's scrcpy/libwebrtc.
2047
+ const outboundCameraTransceiver =
2048
+ platform === 'ios'
2049
+ ? peerConnection.addTransceiver('video', {
2050
+ direction: 'sendonly',
2051
+ })
2052
+ : null;
2053
+ outboundCameraSenderRef.current = outboundCameraTransceiver?.sender ?? null;
2050
2054
 
2051
2055
  // As hardware encoder, we use H265 for iOS and VP9 for Android.
2052
2056
  // We make sure these two are the first ones in the list.
@@ -2092,7 +2096,7 @@ export const RemoteControl = forwardRef<RemoteControlHandle, RemoteControlProps>
2092
2096
  // Chrome falls back to *software* HEVC, not H.264 — no spec
2093
2097
  // hook for runtime re-negotiation. The host-side stats
2094
2098
  // logger will surface this as decoder=ffmpeg / hw=false.
2095
- if (RTCRtpSender.getCapabilities) {
2099
+ if (outboundCameraTransceiver && RTCRtpSender.getCapabilities) {
2096
2100
  const senderCaps = RTCRtpSender.getCapabilities('video');
2097
2101
  if (senderCaps && senderCaps.codecs) {
2098
2102
  const vtH264Profiles = new Set(['42e01f', '42e028', '640c1f']);