@decentnetwork/lan 0.1.176 → 0.1.178

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;
@@ -1799,7 +1800,8 @@ function CallOverlay({ T, ctl, peers }) {
1799
1800
  if (!call) return null;
1800
1801
  const peer = (peers || []).find((p) => p.id === call.peerId) || { id: call.peerId };
1801
1802
  const name = peer.alias || peer.name || shortKey(call.peerId, 10, 6);
1802
- const video = call.video;
1803
+ const streamHasVideo = (s) => !!(s && s.getVideoTracks && s.getVideoTracks().length > 0);
1804
+ const video = call.video || ctl.sharing || streamHasVideo(ctl.remoteStream) || streamHasVideo(ctl.localStream);
1803
1805
  const mm = String(Math.floor(secs / 60)).padStart(2, "0");
1804
1806
  const ss = String(secs % 60).padStart(2, "0");
1805
1807
  const stateLabel = connected ? mm + ":" + ss : call.state === "ringing" ? T && T.ringing || "ringing\u2026" : call.state === "connecting" ? T && T.connecting || "connecting\u2026" : call.state;
@@ -1810,7 +1812,33 @@ function CallOverlay({ T, ctl, peers }) {
1810
1812
  };
1811
1813
  const canShare = connected && typeof navigator !== "undefined" && navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia;
1812
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 }));
1813
- 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)));
1814
1842
  }
1815
1843
  Object.assign(window, { useCallController, CallOverlay, IncomingCallModal });
1816
1844
  const DK_DEFAULTS = (
@@ -535,8 +535,14 @@ var PeerWebRTC = (() => {
535
535
  }
536
536
  };
537
537
  pc.ontrack = (ev) => {
538
- const stream = __privateMethod(this, _CallEngine_instances, ensureRemoteStream_fn).call(this, session, ev.track);
539
- this.emit("remoteStream", session.callId, stream);
538
+ __privateMethod(this, _CallEngine_instances, ensureRemoteStream_fn).call(this, session, ev.track);
539
+ const snapshot = new MediaStream(session.remoteStream.getTracks());
540
+ this.emit("remoteStream", session.callId, snapshot);
541
+ ev.track.addEventListener("unmute", () => {
542
+ if (!session.remoteStream)
543
+ return;
544
+ this.emit("remoteStream", session.callId, new MediaStream(session.remoteStream.getTracks()));
545
+ });
540
546
  };
541
547
  pc.oniceconnectionstatechange = () => {
542
548
  switch (pc.iceConnectionState) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/lan",
3
- "version": "0.1.176",
3
+ "version": "0.1.178",
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.8",
83
+ "@decentnetwork/peer-webrtc": "^0.2.9",
84
84
  "ink": "^5.2.1",
85
85
  "js-yaml": "^4.1.0",
86
86
  "node-forge": "^1.4.0",