@decentnetwork/lan 0.1.175 → 0.1.177
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/bin/tun-helper-darwin-amd64 +0 -0
- package/bin/tun-helper-darwin-arm64 +0 -0
- package/bin/tun-helper-linux-amd64 +0 -0
- package/bin/tun-helper-linux-arm64 +0 -0
- package/dist/ui/desktop/app.js +25 -4
- package/dist/ui/desktop/vendor/peer-webrtc.js +109 -10
- package/dist/ui/server.js +6 -4
- package/package.json +2 -2
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/ui/desktop/app.js
CHANGED
|
@@ -20,6 +20,7 @@ const ICON_PATHS = {
|
|
|
20
20
|
qr: '<rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><path d="M14 14h3v3M21 14v.01M14 21h.01M17 21h.01M21 17v4"/>',
|
|
21
21
|
// ---- app tiles ----
|
|
22
22
|
video: '<path d="m22 8.5-5.4 3.5 5.4 3.5V8.5Z"/><rect x="2" y="6" width="14.5" height="12" rx="3"/>',
|
|
23
|
+
monitor: '<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/>',
|
|
23
24
|
sparkles: '<path d="M12 3.5 13.6 9 19 10.5 13.6 12 12 17.5 10.4 12 5 10.5 10.4 9 12 3.5Z"/><path d="M19 3.5v3.2M20.6 5.1h-3.2"/><path d="M5 16v2.4M6.2 17.2H3.8"/>',
|
|
24
25
|
mic: '<rect x="9" y="2.5" width="6" height="11.5" rx="3"/><path d="M5.5 11a6.5 6.5 0 0 0 13 0"/><path d="M12 17.5v3.5"/>',
|
|
25
26
|
flask: '<path d="M9.5 3v6.2L4.8 17.4A2 2 0 0 0 6.5 20.5h11a2 2 0 0 0 1.7-3.1L14.5 9.2V3"/><path d="M8.5 3h7"/><path d="M7.3 15h9.4"/>',
|
|
@@ -1596,6 +1597,7 @@ function useCallController(selfId) {
|
|
|
1596
1597
|
const [active, setActive] = React.useState(null);
|
|
1597
1598
|
const [localStream, setLocalStream] = React.useState(null);
|
|
1598
1599
|
const [remoteStream, setRemoteStream] = React.useState(null);
|
|
1600
|
+
const [sharing, setSharing] = React.useState(false);
|
|
1599
1601
|
const engineRef = React.useRef(null);
|
|
1600
1602
|
React.useEffect(() => {
|
|
1601
1603
|
if (!selfId || engineRef.current) return;
|
|
@@ -1610,12 +1612,19 @@ function useCallController(selfId) {
|
|
|
1610
1612
|
signaling,
|
|
1611
1613
|
createPeerConnection: (c) => new RTCPeerConnection(c),
|
|
1612
1614
|
getLocalMedia: (k) => navigator.mediaDevices.getUserMedia({ audio: k.audio, video: k.video }),
|
|
1615
|
+
getDisplayMedia: (c) => navigator.mediaDevices.getDisplayMedia(c || { video: true, audio: false }),
|
|
1613
1616
|
iceServers: CALL_ICE_SERVERS,
|
|
1614
1617
|
logger: (m) => console.log(m)
|
|
1615
1618
|
});
|
|
1616
1619
|
engine.on("incomingCall", (info) => setIncoming(info));
|
|
1617
1620
|
engine.on("stateChanged", (info, state) => setActive((a) => a && a.callId === info.callId ? { ...a, state } : a));
|
|
1618
|
-
engine.on("localStream", (id, s) =>
|
|
1621
|
+
engine.on("localStream", (id, s) => {
|
|
1622
|
+
setLocalStream(s);
|
|
1623
|
+
try {
|
|
1624
|
+
setSharing(engine.isSharingScreen(id));
|
|
1625
|
+
} catch (e) {
|
|
1626
|
+
}
|
|
1627
|
+
});
|
|
1619
1628
|
engine.on("remoteStream", (id, s) => setRemoteStream(s));
|
|
1620
1629
|
engine.on("ended", (id) => {
|
|
1621
1630
|
setActive((a) => a && a.callId === id ? null : a);
|
|
@@ -1675,11 +1684,21 @@ function useCallController(selfId) {
|
|
|
1675
1684
|
const eng = engineRef.current, a = active;
|
|
1676
1685
|
if (eng && a && a.callId) eng.setLocalTrackEnabled(a.callId, "audio", !m);
|
|
1677
1686
|
}, [active]);
|
|
1687
|
+
const toggleShare = React.useCallback(async () => {
|
|
1688
|
+
const eng = engineRef.current, a = active;
|
|
1689
|
+
if (!eng || !a || !a.callId) return;
|
|
1690
|
+
try {
|
|
1691
|
+
if (eng.isSharingScreen(a.callId)) await eng.stopScreenShare(a.callId);
|
|
1692
|
+
else await eng.shareScreen(a.callId);
|
|
1693
|
+
} catch (e) {
|
|
1694
|
+
if (e && e.name !== "NotAllowedError") alert("Screen share failed: " + (e && e.message || e));
|
|
1695
|
+
}
|
|
1696
|
+
}, [active]);
|
|
1678
1697
|
const setVideoOn = React.useCallback((v) => {
|
|
1679
1698
|
const eng = engineRef.current, a = active;
|
|
1680
1699
|
if (eng && a && a.callId) eng.setLocalTrackEnabled(a.callId, "video", v);
|
|
1681
1700
|
}, [active]);
|
|
1682
|
-
return { incoming, active, localStream, remoteStream, start, accept, reject, hangup, setMuted, setVideoOn };
|
|
1701
|
+
return { incoming, active, localStream, remoteStream, sharing, start, accept, reject, hangup, setMuted, setVideoOn, toggleShare };
|
|
1683
1702
|
}
|
|
1684
1703
|
function VideoSurface({ stream, muted, style }) {
|
|
1685
1704
|
const ref = React.useCallback((el) => {
|
|
@@ -1780,7 +1799,8 @@ function CallOverlay({ T, ctl, peers }) {
|
|
|
1780
1799
|
if (!call) return null;
|
|
1781
1800
|
const peer = (peers || []).find((p) => p.id === call.peerId) || { id: call.peerId };
|
|
1782
1801
|
const name = peer.alias || peer.name || shortKey(call.peerId, 10, 6);
|
|
1783
|
-
const
|
|
1802
|
+
const streamHasVideo = (s) => !!(s && s.getVideoTracks && s.getVideoTracks().length > 0);
|
|
1803
|
+
const video = call.video || ctl.sharing || streamHasVideo(ctl.remoteStream) || streamHasVideo(ctl.localStream);
|
|
1784
1804
|
const mm = String(Math.floor(secs / 60)).padStart(2, "0");
|
|
1785
1805
|
const ss = String(secs % 60).padStart(2, "0");
|
|
1786
1806
|
const stateLabel = connected ? mm + ":" + ss : call.state === "ringing" ? T && T.ringing || "ringing\u2026" : call.state === "connecting" ? T && T.connecting || "connecting\u2026" : call.state;
|
|
@@ -1789,7 +1809,8 @@ function CallOverlay({ T, ctl, peers }) {
|
|
|
1789
1809
|
setMuted(n);
|
|
1790
1810
|
ctl.setMuted(n);
|
|
1791
1811
|
};
|
|
1792
|
-
const
|
|
1812
|
+
const canShare = connected && typeof navigator !== "undefined" && navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia;
|
|
1813
|
+
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 }));
|
|
1793
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)));
|
|
1794
1815
|
}
|
|
1795
1816
|
Object.assign(window, { useCallController, CallOverlay, IncomingCallModal });
|
|
@@ -153,7 +153,7 @@ var PeerWebRTC = (() => {
|
|
|
153
153
|
var DEFAULT_ICE_SERVERS = [
|
|
154
154
|
{ urls: "stun:stun.l.google.com:19302" }
|
|
155
155
|
];
|
|
156
|
-
var _opts, _iceServers, _sessions, _CallEngine_instances, handleSignal_fn, onOffer_fn, onAnswer_fn, onCandidate_fn, onBye_fn, onAuxiliary_fn, createSession_fn, wirePeerConnection_fn, ensureRemoteStream_fn, attachLocalMedia_fn, flushLocalCandidates_fn, drainRemoteCandidates_fn, send_fn, sendByeSafe_fn, cleanup_fn, setState_fn, normId_fn, _toInfo, newCallId_fn, log_fn;
|
|
156
|
+
var _opts, _iceServers, _sessions, _CallEngine_instances, setOutgoingVideoTrack_fn, renegotiate_fn, handleSignal_fn, onOffer_fn, answerRenegotiation_fn, onAnswer_fn, onCandidate_fn, onBye_fn, onAuxiliary_fn, createSession_fn, wirePeerConnection_fn, ensureRemoteStream_fn, attachLocalMedia_fn, flushLocalCandidates_fn, drainRemoteCandidates_fn, send_fn, sendByeSafe_fn, cleanup_fn, setState_fn, normId_fn, _toInfo, newCallId_fn, log_fn;
|
|
157
157
|
var CallEngine = class extends Emitter {
|
|
158
158
|
constructor(opts) {
|
|
159
159
|
var _a;
|
|
@@ -286,6 +286,50 @@ var PeerWebRTC = (() => {
|
|
|
286
286
|
track.enabled = enabled;
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
|
+
/** Start sharing the screen on an active call. Captures the display, sends it
|
|
290
|
+
* as the outgoing video track (replacing the camera if any), and renegotiates
|
|
291
|
+
* so the peer receives it. Works even with no camera. Ending the OS "stop
|
|
292
|
+
* sharing" prompt reverts automatically. */
|
|
293
|
+
async shareScreen(callId) {
|
|
294
|
+
const session = __privateGet(this, _sessions).get(__privateMethod(this, _CallEngine_instances, normId_fn).call(this, callId));
|
|
295
|
+
if (!session)
|
|
296
|
+
throw new Error(`shareScreen: no call ${callId}`);
|
|
297
|
+
if (!__privateGet(this, _opts).getDisplayMedia)
|
|
298
|
+
throw new Error("screen share not supported here");
|
|
299
|
+
const screen = await __privateGet(this, _opts).getDisplayMedia({ video: true, audio: false });
|
|
300
|
+
const track = screen.getVideoTracks()[0];
|
|
301
|
+
if (!track)
|
|
302
|
+
return;
|
|
303
|
+
if (!session.screenStream)
|
|
304
|
+
session.cameraStream = session.localStream;
|
|
305
|
+
session.screenStream = screen;
|
|
306
|
+
await __privateMethod(this, _CallEngine_instances, setOutgoingVideoTrack_fn).call(this, session, track, screen);
|
|
307
|
+
track.addEventListener("ended", () => {
|
|
308
|
+
void this.stopScreenShare(callId);
|
|
309
|
+
});
|
|
310
|
+
await __privateMethod(this, _CallEngine_instances, renegotiate_fn).call(this, session);
|
|
311
|
+
__privateMethod(this, _CallEngine_instances, log_fn).call(this, `shareScreen started for ${callId}`);
|
|
312
|
+
}
|
|
313
|
+
/** Stop screen sharing; restore the camera track if there was one. */
|
|
314
|
+
async stopScreenShare(callId) {
|
|
315
|
+
var _a;
|
|
316
|
+
const session = __privateGet(this, _sessions).get(__privateMethod(this, _CallEngine_instances, normId_fn).call(this, callId));
|
|
317
|
+
if (!(session == null ? void 0 : session.screenStream))
|
|
318
|
+
return;
|
|
319
|
+
for (const t of session.screenStream.getTracks())
|
|
320
|
+
t.stop();
|
|
321
|
+
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;
|
|
325
|
+
await __privateMethod(this, _CallEngine_instances, renegotiate_fn).call(this, session);
|
|
326
|
+
__privateMethod(this, _CallEngine_instances, log_fn).call(this, `shareScreen stopped for ${callId}`);
|
|
327
|
+
}
|
|
328
|
+
/** True when the call is currently sharing the screen. */
|
|
329
|
+
isSharingScreen(callId) {
|
|
330
|
+
var _a;
|
|
331
|
+
return !!((_a = __privateGet(this, _sessions).get(__privateMethod(this, _CallEngine_instances, normId_fn).call(this, callId))) == null ? void 0 : _a.screenStream);
|
|
332
|
+
}
|
|
289
333
|
/** Tear down every call (e.g. on app shutdown). */
|
|
290
334
|
dispose() {
|
|
291
335
|
for (const session of [...__privateGet(this, _sessions).values()]) {
|
|
@@ -298,6 +342,44 @@ var PeerWebRTC = (() => {
|
|
|
298
342
|
_iceServers = new WeakMap();
|
|
299
343
|
_sessions = new WeakMap();
|
|
300
344
|
_CallEngine_instances = new WeakSet();
|
|
345
|
+
setOutgoingVideoTrack_fn = async function(session, track, stream) {
|
|
346
|
+
var _a;
|
|
347
|
+
const transceivers = session.pc.getTransceivers();
|
|
348
|
+
const tr = (_a = transceivers.find((t) => {
|
|
349
|
+
var _a2;
|
|
350
|
+
return ((_a2 = t.sender.track) == null ? void 0 : _a2.kind) === "video";
|
|
351
|
+
})) != null ? _a : transceivers.find((t) => {
|
|
352
|
+
var _a2;
|
|
353
|
+
return ((_a2 = t.receiver.track) == null ? void 0 : _a2.kind) === "video";
|
|
354
|
+
});
|
|
355
|
+
if (tr) {
|
|
356
|
+
await tr.sender.replaceTrack(track);
|
|
357
|
+
if (track) {
|
|
358
|
+
if (tr.direction === "recvonly")
|
|
359
|
+
tr.direction = "sendrecv";
|
|
360
|
+
else if (tr.direction === "inactive")
|
|
361
|
+
tr.direction = "sendonly";
|
|
362
|
+
}
|
|
363
|
+
} else if (track && stream) {
|
|
364
|
+
session.pc.addTrack(track, stream);
|
|
365
|
+
}
|
|
366
|
+
session.localStream = stream;
|
|
367
|
+
this.emit("localStream", session.callId, stream != null ? stream : new MediaStream());
|
|
368
|
+
};
|
|
369
|
+
renegotiate_fn = async function(session) {
|
|
370
|
+
try {
|
|
371
|
+
const offer = await session.pc.createOffer();
|
|
372
|
+
await session.pc.setLocalDescription(offer);
|
|
373
|
+
const options = [];
|
|
374
|
+
if (session.audio)
|
|
375
|
+
options.push("audio");
|
|
376
|
+
if (session.video || session.screenStream)
|
|
377
|
+
options.push("video");
|
|
378
|
+
await __privateMethod(this, _CallEngine_instances, send_fn).call(this, session.peerId, { type: "offer", sdp: offer.sdp, options, callId: session.callId });
|
|
379
|
+
} catch (err) {
|
|
380
|
+
__privateMethod(this, _CallEngine_instances, log_fn).call(this, `renegotiate failed: ${err.message}`);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
301
383
|
// ---- internals -------------------------------------------------------
|
|
302
384
|
handleSignal_fn = function(peerId, signal) {
|
|
303
385
|
switch (signal.type) {
|
|
@@ -328,7 +410,10 @@ var PeerWebRTC = (() => {
|
|
|
328
410
|
return;
|
|
329
411
|
const existing = __privateGet(this, _sessions).get(__privateMethod(this, _CallEngine_instances, normId_fn).call(this, signal.callId));
|
|
330
412
|
if (existing) {
|
|
331
|
-
existing.
|
|
413
|
+
if (existing.hasReceivedSdp)
|
|
414
|
+
void __privateMethod(this, _CallEngine_instances, answerRenegotiation_fn).call(this, existing, signal.sdp);
|
|
415
|
+
else
|
|
416
|
+
existing.pendingOffer = signal.sdp;
|
|
332
417
|
return;
|
|
333
418
|
}
|
|
334
419
|
const audio = signal.options ? signal.options.includes("audio") : true;
|
|
@@ -339,6 +424,16 @@ var PeerWebRTC = (() => {
|
|
|
339
424
|
this.emit("incomingCall", __privateGet(this, _toInfo).call(this, session));
|
|
340
425
|
__privateMethod(this, _CallEngine_instances, log_fn).call(this, `incoming call from ${peerId} callId=${signal.callId} audio=${audio} video=${video}`);
|
|
341
426
|
};
|
|
427
|
+
answerRenegotiation_fn = async function(session, sdp) {
|
|
428
|
+
try {
|
|
429
|
+
await session.pc.setRemoteDescription({ type: "offer", sdp });
|
|
430
|
+
const answer = await session.pc.createAnswer();
|
|
431
|
+
await session.pc.setLocalDescription(answer);
|
|
432
|
+
await __privateMethod(this, _CallEngine_instances, send_fn).call(this, session.peerId, { type: "answer", sdp: answer.sdp, callId: session.callId });
|
|
433
|
+
} catch (err) {
|
|
434
|
+
__privateMethod(this, _CallEngine_instances, log_fn).call(this, `renegotiation answer failed: ${err.message}`);
|
|
435
|
+
}
|
|
436
|
+
};
|
|
342
437
|
onAnswer_fn = function(peerId, signal) {
|
|
343
438
|
const session = __privateGet(this, _sessions).get(__privateMethod(this, _CallEngine_instances, normId_fn).call(this, signal.callId));
|
|
344
439
|
if (!session || !signal.sdp)
|
|
@@ -348,7 +443,8 @@ var PeerWebRTC = (() => {
|
|
|
348
443
|
try {
|
|
349
444
|
await session.pc.setRemoteDescription({ type: "answer", sdp: signal.sdp });
|
|
350
445
|
session.hasReceivedSdp = true;
|
|
351
|
-
|
|
446
|
+
if (session.state !== "connected")
|
|
447
|
+
__privateMethod(this, _CallEngine_instances, setState_fn).call(this, session, "connecting");
|
|
352
448
|
await __privateMethod(this, _CallEngine_instances, drainRemoteCandidates_fn).call(this, session);
|
|
353
449
|
await __privateMethod(this, _CallEngine_instances, flushLocalCandidates_fn).call(this, session);
|
|
354
450
|
} catch (err) {
|
|
@@ -439,12 +535,14 @@ var PeerWebRTC = (() => {
|
|
|
439
535
|
}
|
|
440
536
|
};
|
|
441
537
|
pc.ontrack = (ev) => {
|
|
442
|
-
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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
|
+
});
|
|
448
546
|
};
|
|
449
547
|
pc.oniceconnectionstatechange = () => {
|
|
450
548
|
switch (pc.iceConnectionState) {
|
|
@@ -471,7 +569,8 @@ var PeerWebRTC = (() => {
|
|
|
471
569
|
ensureRemoteStream_fn = function(session, track) {
|
|
472
570
|
if (!session.remoteStream)
|
|
473
571
|
session.remoteStream = new MediaStream();
|
|
474
|
-
session.remoteStream.
|
|
572
|
+
if (!session.remoteStream.getTracks().includes(track))
|
|
573
|
+
session.remoteStream.addTrack(track);
|
|
475
574
|
return session.remoteStream;
|
|
476
575
|
};
|
|
477
576
|
attachLocalMedia_fn = async function(session) {
|
package/dist/ui/server.js
CHANGED
|
@@ -198,11 +198,13 @@ export function startFriendUi(opts) {
|
|
|
198
198
|
const file = join(DESKTOP_DIR, rel);
|
|
199
199
|
// Guard against path traversal: resolved file must stay under DESKTOP_DIR.
|
|
200
200
|
if (existsSync(file) && file.startsWith(DESKTOP_DIR)) {
|
|
201
|
-
// app.js
|
|
202
|
-
//
|
|
201
|
+
// app.js AND vendor/peer-webrtc.js change on every rebuild / SDK bump
|
|
202
|
+
// → never cache them, or the browser keeps running stale call/UI code
|
|
203
|
+
// (this silently defeated every peer-webrtc fix). Only the truly stable
|
|
204
|
+
// vendored libs (React UMD) may cache.
|
|
203
205
|
const headers = { "content-type": "application/javascript; charset=utf-8" };
|
|
204
|
-
|
|
205
|
-
|
|
206
|
+
const volatile = rel === "app.js" || rel.includes("peer-webrtc");
|
|
207
|
+
headers["cache-control"] = volatile ? "no-store" : "public, max-age=31536000";
|
|
206
208
|
res.writeHead(200, headers);
|
|
207
209
|
res.end(readFileSync(file));
|
|
208
210
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/lan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.177",
|
|
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.
|
|
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",
|