@decentnetwork/lan 0.1.177 → 0.1.179

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.
Binary file
Binary file
Binary file
Binary file
@@ -1784,6 +1784,7 @@ function CallOverlay({ T, ctl, peers }) {
1784
1784
  const call = ctl.active;
1785
1785
  const [secs, setSecs] = React.useState(0);
1786
1786
  const [muted, setMuted] = React.useState(false);
1787
+ const [featured, setFeatured] = React.useState("remote");
1787
1788
  const connected = call && call.state === "connected";
1788
1789
  React.useEffect(() => {
1789
1790
  if (!connected) return;
@@ -1811,7 +1812,33 @@ function CallOverlay({ T, ctl, peers }) {
1811
1812
  };
1812
1813
  const canShare = connected && typeof navigator !== "undefined" && navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia;
1813
1814
  const controls = /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 18, alignItems: "flex-start" } }, /* @__PURE__ */ React.createElement(CallBtn, { icon: muted ? "micFill" : "mic", label: muted ? T && T.unmute || "unmute" : T && T.mute || "mute", active: muted, onClick: toggleMute }), canShare && /* @__PURE__ */ React.createElement(CallBtn, { icon: "monitor", label: ctl.sharing ? T && T.stopShare || "stop share" : T && T.share || "share", active: ctl.sharing, onClick: ctl.toggleShare }), /* @__PURE__ */ React.createElement(CallBtn, { icon: "phone", label: T && T.endCall || "end", danger: true, wide: true, onClick: ctl.hangup }));
1814
- return /* @__PURE__ */ React.createElement("div", { style: { position: "fixed", inset: 0, zIndex: 70, background: "var(--bg)", display: "flex", flexDirection: "column" } }, ctl.remoteStream && /* @__PURE__ */ React.createElement(AudioSink, { stream: ctl.remoteStream }), /* @__PURE__ */ React.createElement("div", { style: { height: 52, flexShrink: 0, display: "flex", alignItems: "center", gap: 10, padding: "0 18px", borderBottom: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement(Icon, { name: video ? "video" : "phone", size: 16, color: "var(--accent)", stroke: 2 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 13, fontWeight: 700, color: "var(--text)" } }, name), /* @__PURE__ */ React.createElement(Tag, { tone: connected ? "ok" : "warn" }, stateLabel), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), connected && /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 14, fontWeight: 700, color: "var(--text)", letterSpacing: 1 } }, mm, ":", ss)), video ? /* @__PURE__ */ React.createElement("div", { style: { flex: 1, position: "relative", padding: 18 } }, /* @__PURE__ */ React.createElement("div", { style: { position: "absolute", inset: 18, borderRadius: 14, overflow: "hidden", background: "#000", border: "1px solid var(--line)" } }, ctl.remoteStream ? /* @__PURE__ */ React.createElement(VideoSurface, { stream: ctl.remoteStream, muted: true, style: { width: "100%", height: "100%", objectFit: "cover" } }) : /* @__PURE__ */ React.createElement("div", { style: { width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center" } }, /* @__PURE__ */ React.createElement(DkIdenticon, { seed: call.peerId, size: 92, radius: 22 }))), /* @__PURE__ */ React.createElement("div", { style: { position: "absolute", right: 30, bottom: 30, width: 220, height: 148, borderRadius: 12, overflow: "hidden", background: "#000", border: "1px solid var(--line)" } }, ctl.localStream ? /* @__PURE__ */ React.createElement(VideoSurface, { stream: ctl.localStream, muted: true, style: { width: "100%", height: "100%", objectFit: "cover", transform: "scaleX(-1)" } }) : /* @__PURE__ */ React.createElement("div", { style: { width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center" } }, /* @__PURE__ */ React.createElement(Icon, { name: "userRound", size: 30, color: "var(--faint)", stroke: 1.4 }))), /* @__PURE__ */ React.createElement("div", { style: { position: "absolute", left: 0, right: 0, bottom: 30, display: "flex", justifyContent: "center" } }, /* @__PURE__ */ React.createElement("div", { style: { padding: "16px 26px", borderRadius: 20, background: "color-mix(in oklab, var(--panel), transparent 8%)", border: "1px solid var(--line)" } }, controls))) : /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 22 } }, /* @__PURE__ */ React.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center", justifyContent: "center" } }, !connected && /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", width: 150, height: 150, borderRadius: 999, border: "2px solid var(--accent)", animation: "dkpulse 2s ease-out infinite" } }), /* @__PURE__ */ React.createElement(DkIdenticon, { seed: call.peerId, size: 108, radius: 26 })), /* @__PURE__ */ React.createElement("div", { style: { textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 22, fontWeight: 700, color: "var(--text)" } }, name), /* @__PURE__ */ React.createElement("div", { style: { marginTop: 8, fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--faint)" } }, stateLabel)), /* @__PURE__ */ React.createElement("div", { style: { marginTop: 18 } }, controls)));
1815
+ return /* @__PURE__ */ React.createElement("div", { style: { position: "fixed", inset: 0, zIndex: 70, background: "var(--bg)", display: "flex", flexDirection: "column" } }, ctl.remoteStream && /* @__PURE__ */ React.createElement(AudioSink, { stream: ctl.remoteStream }), /* @__PURE__ */ React.createElement("div", { style: { height: 52, flexShrink: 0, display: "flex", alignItems: "center", gap: 10, padding: "0 18px", borderBottom: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement(Icon, { name: video ? "video" : "phone", size: 16, color: "var(--accent)", stroke: 2 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 13, fontWeight: 700, color: "var(--text)" } }, name), /* @__PURE__ */ React.createElement(Tag, { tone: connected ? "ok" : "warn" }, stateLabel), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), connected && /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 14, fontWeight: 700, color: "var(--text)", letterSpacing: 1 } }, mm, ":", ss)), video ? (() => {
1816
+ const tiles = [
1817
+ { key: "remote", stream: ctl.remoteStream, label: name, mirror: false },
1818
+ { key: "local", stream: ctl.localStream, label: ctl.sharing ? T && T.yourScreen || "Your screen" : T && T.you || "You", mirror: !ctl.sharing }
1819
+ ].filter((t) => streamHasVideo(t.stream));
1820
+ const feat = tiles.find((t) => t.key === featured) || tiles[0] || { key: "remote", stream: ctl.remoteStream, label: name };
1821
+ return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, position: "relative", padding: 18, display: "flex", flexDirection: "column" } }, /* @__PURE__ */ React.createElement("div", { style: { flex: 1, position: "relative", borderRadius: 14, overflow: "hidden", background: "#000", border: "1px solid var(--line)" } }, feat.stream ? /* @__PURE__ */ React.createElement(VideoSurface, { stream: feat.stream, muted: true, style: { width: "100%", height: "100%", objectFit: "contain", transform: feat.mirror ? "scaleX(-1)" : "none" } }) : /* @__PURE__ */ React.createElement("div", { style: { width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center" } }, /* @__PURE__ */ React.createElement(DkIdenticon, { seed: call.peerId, size: 92, radius: 22 })), /* @__PURE__ */ React.createElement("div", { style: { position: "absolute", left: 14, top: 12, padding: "4px 10px", borderRadius: 8, background: "rgba(0,0,0,.55)", color: "#fff", fontFamily: "var(--mono)", fontSize: 12, fontWeight: 600 } }, feat.label)), tiles.length > 0 && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 12, marginTop: 12, justifyContent: "center", flexWrap: "wrap" } }, tiles.map((t) => /* @__PURE__ */ React.createElement(
1822
+ "button",
1823
+ {
1824
+ key: t.key,
1825
+ onClick: () => setFeatured(t.key),
1826
+ style: {
1827
+ position: "relative",
1828
+ width: 150,
1829
+ height: 96,
1830
+ borderRadius: 10,
1831
+ overflow: "hidden",
1832
+ background: "#000",
1833
+ cursor: "pointer",
1834
+ padding: 0,
1835
+ border: feat.key === t.key ? "2px solid var(--accent)" : "1px solid var(--line)"
1836
+ }
1837
+ },
1838
+ /* @__PURE__ */ React.createElement(VideoSurface, { stream: t.stream, muted: true, style: { width: "100%", height: "100%", objectFit: "cover", transform: t.mirror ? "scaleX(-1)" : "none" } }),
1839
+ /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", left: 6, bottom: 5, padding: "2px 6px", borderRadius: 6, background: "rgba(0,0,0,.6)", color: "#fff", fontSize: 10, fontFamily: "var(--mono)" } }, t.label)
1840
+ ))), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "center", marginTop: 14 } }, /* @__PURE__ */ React.createElement("div", { style: { padding: "16px 26px", borderRadius: 20, background: "color-mix(in oklab, var(--panel), transparent 8%)", border: "1px solid var(--line)" } }, controls)));
1841
+ })() : /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 22 } }, /* @__PURE__ */ React.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center", justifyContent: "center" } }, !connected && /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", width: 150, height: 150, borderRadius: 999, border: "2px solid var(--accent)", animation: "dkpulse 2s ease-out infinite" } }), /* @__PURE__ */ React.createElement(DkIdenticon, { seed: call.peerId, size: 108, radius: 26 })), /* @__PURE__ */ React.createElement("div", { style: { textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 22, fontWeight: 700, color: "var(--text)" } }, name), /* @__PURE__ */ React.createElement("div", { style: { marginTop: 8, fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--faint)" } }, stateLabel)), /* @__PURE__ */ React.createElement("div", { style: { marginTop: 18 } }, controls)));
1815
1842
  }
1816
1843
  Object.assign(window, { useCallController, CallOverlay, IncomingCallModal });
1817
1844
  const DK_DEFAULTS = (
@@ -291,6 +291,7 @@ var PeerWebRTC = (() => {
291
291
  * so the peer receives it. Works even with no camera. Ending the OS "stop
292
292
  * sharing" prompt reverts automatically. */
293
293
  async shareScreen(callId) {
294
+ var _a, _b, _c;
294
295
  const session = __privateGet(this, _sessions).get(__privateMethod(this, _CallEngine_instances, normId_fn).call(this, callId));
295
296
  if (!session)
296
297
  throw new Error(`shareScreen: no call ${callId}`);
@@ -300,8 +301,11 @@ var PeerWebRTC = (() => {
300
301
  const track = screen.getVideoTracks()[0];
301
302
  if (!track)
302
303
  return;
303
- if (!session.screenStream)
304
- session.cameraStream = session.localStream;
304
+ if (!session.screenStream) {
305
+ session.hadCamera = !!((_a = session.localStream) == null ? void 0 : _a.getVideoTracks().length);
306
+ for (const t of (_c = (_b = session.localStream) == null ? void 0 : _b.getVideoTracks()) != null ? _c : [])
307
+ t.stop();
308
+ }
305
309
  session.screenStream = screen;
306
310
  await __privateMethod(this, _CallEngine_instances, setOutgoingVideoTrack_fn).call(this, session, track, screen);
307
311
  track.addEventListener("ended", () => {
@@ -310,7 +314,7 @@ var PeerWebRTC = (() => {
310
314
  await __privateMethod(this, _CallEngine_instances, renegotiate_fn).call(this, session);
311
315
  __privateMethod(this, _CallEngine_instances, log_fn).call(this, `shareScreen started for ${callId}`);
312
316
  }
313
- /** Stop screen sharing; restore the camera track if there was one. */
317
+ /** Stop screen sharing; re-acquire the camera if there was one. */
314
318
  async stopScreenShare(callId) {
315
319
  var _a;
316
320
  const session = __privateGet(this, _sessions).get(__privateMethod(this, _CallEngine_instances, normId_fn).call(this, callId));
@@ -319,9 +323,12 @@ var PeerWebRTC = (() => {
319
323
  for (const t of session.screenStream.getTracks())
320
324
  t.stop();
321
325
  session.screenStream = void 0;
322
- const camTrack = (_a = session.cameraStream) == null ? void 0 : _a.getVideoTracks()[0];
323
- await __privateMethod(this, _CallEngine_instances, setOutgoingVideoTrack_fn).call(this, session, camTrack != null ? camTrack : null, session.cameraStream);
324
- session.cameraStream = void 0;
326
+ let cam;
327
+ if (session.hadCamera && __privateGet(this, _opts).getLocalMedia) {
328
+ cam = await __privateGet(this, _opts).getLocalMedia({ audio: false, video: true }).catch(() => void 0);
329
+ }
330
+ session.hadCamera = false;
331
+ await __privateMethod(this, _CallEngine_instances, setOutgoingVideoTrack_fn).call(this, session, (_a = cam == null ? void 0 : cam.getVideoTracks()[0]) != null ? _a : null, cam);
325
332
  await __privateMethod(this, _CallEngine_instances, renegotiate_fn).call(this, session);
326
333
  __privateMethod(this, _CallEngine_instances, log_fn).call(this, `shareScreen stopped for ${callId}`);
327
334
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/lan",
3
- "version": "0.1.177",
3
+ "version": "0.1.179",
4
4
  "description": "Private virtual LAN for self-hosted services and AI agents, built on Elastos Carrier. NAT-traversal, name service, ACL, all over a peer-to-peer mesh — no public IP required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -80,7 +80,7 @@
80
80
  "dependencies": {
81
81
  "@decentnetwork/dora": "^0.1.13",
82
82
  "@decentnetwork/peer": "^0.1.91",
83
- "@decentnetwork/peer-webrtc": "^0.2.9",
83
+ "@decentnetwork/peer-webrtc": "^0.2.10",
84
84
  "ink": "^5.2.1",
85
85
  "js-yaml": "^4.1.0",
86
86
  "node-forge": "^1.4.0",