@decentnetwork/lan 0.1.223 → 0.1.224
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/ui/desktop/app.js +47 -11
- package/package.json +1 -1
package/dist/ui/desktop/app.js
CHANGED
|
@@ -1547,9 +1547,11 @@ function Conversation({ T, peer, lang, thread: threadProp, onSend, onSendFile, o
|
|
|
1547
1547
|
if (!files || !files.length || !sender) return;
|
|
1548
1548
|
const file = files[0];
|
|
1549
1549
|
followScrollRef.current = true;
|
|
1550
|
-
setSending({ name: file.name, via: mode });
|
|
1551
|
-
|
|
1550
|
+
setSending({ name: file.name, via: mode, pct: 0 });
|
|
1551
|
+
const onProgress = (p) => setSending((s) => s && s.name === file.name ? { ...s, pct: p && p.pct } : s);
|
|
1552
|
+
Promise.resolve(sender(file, onProgress)).then((r) => {
|
|
1552
1553
|
if (r && r.ok === false) window.alert((lang === "zh" ? "\u53D1\u9001\u5931\u8D25: " : "Send failed: ") + (r.error || ""));
|
|
1554
|
+
else showFlash("ok", mode === "webrtc" ? lang === "zh" ? "WebRTC \u6587\u4EF6\u53D1\u9001\u5B8C\u6210" : "WebRTC file sent" : lang === "zh" ? "\u6587\u4EF6\u5DF2\u63D0\u4EA4\u53D1\u9001" : "File send started");
|
|
1553
1555
|
}).finally(() => {
|
|
1554
1556
|
setSending(null);
|
|
1555
1557
|
requestAnimationFrame(scrollToBottom);
|
|
@@ -1633,7 +1635,7 @@ function Conversation({ T, peer, lang, thread: threadProp, onSend, onSendFile, o
|
|
|
1633
1635
|
stroke: 2.2,
|
|
1634
1636
|
color: flash.kind === "err" ? "var(--danger)" : "var(--accent)"
|
|
1635
1637
|
}
|
|
1636
|
-
), flash.msg), sending && /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto 8px", display: "flex", alignItems: "center", gap: 8, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "paperclip", size: 13, stroke: 2, color: "var(--accent)" }), (lang === "zh" ? "\u53D1\u9001\u4E2D: " : "Sending: ") + (sending.via === "webrtc" ? "WebRTC \xB7 " : "") + sending.name), /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto", display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React.createElement(
|
|
1638
|
+
), flash.msg), sending && /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto 8px", display: "flex", alignItems: "center", gap: 8, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "paperclip", size: 13, stroke: 2, color: "var(--accent)" }), (lang === "zh" ? "\u53D1\u9001\u4E2D: " : "Sending: ") + (sending.via === "webrtc" ? "WebRTC \xB7 " : "") + sending.name + (sending.pct != null ? " \xB7 " + Math.min(100, Math.max(0, sending.pct)).toFixed(1) + "%" : "")), /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto", display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React.createElement(
|
|
1637
1639
|
"input",
|
|
1638
1640
|
{
|
|
1639
1641
|
ref: fileRef,
|
|
@@ -1897,15 +1899,43 @@ function dkWaitForOpen(dc, timeoutMs, describeState) {
|
|
|
1897
1899
|
}
|
|
1898
1900
|
function dkWaitBufferedLow(dc, highWater) {
|
|
1899
1901
|
if (dc.bufferedAmount < highWater) return Promise.resolve();
|
|
1900
|
-
return new Promise((resolve) => {
|
|
1902
|
+
return new Promise((resolve, reject) => {
|
|
1901
1903
|
const prev = dc.bufferedAmountLowThreshold;
|
|
1902
1904
|
dc.bufferedAmountLowThreshold = Math.floor(highWater / 2);
|
|
1905
|
+
let poll = null;
|
|
1906
|
+
let timer = null;
|
|
1903
1907
|
const done = () => {
|
|
1908
|
+
cleanup();
|
|
1909
|
+
resolve();
|
|
1910
|
+
};
|
|
1911
|
+
const cleanup = () => {
|
|
1912
|
+
if (poll) clearInterval(poll);
|
|
1913
|
+
if (timer) clearTimeout(timer);
|
|
1904
1914
|
dc.removeEventListener("bufferedamountlow", done);
|
|
1915
|
+
dc.removeEventListener("close", onClose);
|
|
1916
|
+
dc.removeEventListener("error", onError);
|
|
1905
1917
|
dc.bufferedAmountLowThreshold = prev;
|
|
1906
|
-
|
|
1918
|
+
};
|
|
1919
|
+
const check = () => {
|
|
1920
|
+
if (dc.readyState !== "open") return onClose();
|
|
1921
|
+
if (dc.bufferedAmount < highWater) done();
|
|
1922
|
+
};
|
|
1923
|
+
const onClose = () => {
|
|
1924
|
+
cleanup();
|
|
1925
|
+
reject(new Error("WebRTC data channel closed while draining"));
|
|
1926
|
+
};
|
|
1927
|
+
const onError = () => {
|
|
1928
|
+
cleanup();
|
|
1929
|
+
reject(new Error("WebRTC data channel failed while draining"));
|
|
1907
1930
|
};
|
|
1908
1931
|
dc.addEventListener("bufferedamountlow", done);
|
|
1932
|
+
dc.addEventListener("close", onClose);
|
|
1933
|
+
dc.addEventListener("error", onError);
|
|
1934
|
+
poll = setInterval(check, 250);
|
|
1935
|
+
timer = setTimeout(() => {
|
|
1936
|
+
cleanup();
|
|
1937
|
+
reject(new Error("WebRTC data channel drain timed out buffered=" + dc.bufferedAmount));
|
|
1938
|
+
}, 3e4);
|
|
1909
1939
|
});
|
|
1910
1940
|
}
|
|
1911
1941
|
function dkFileDownload(blob, name) {
|
|
@@ -2113,10 +2143,12 @@ function useRtcFileController(selfId, onReceivedText) {
|
|
|
2113
2143
|
function RtcFileInbox({ T, peers, ctl }) {
|
|
2114
2144
|
const items = ctl && ctl.incoming || [];
|
|
2115
2145
|
if (!items.length) return null;
|
|
2116
|
-
|
|
2146
|
+
const closeLabel = T && T.receivedClose || "Close";
|
|
2147
|
+
const downloadLabel = T && T.downloadFile || "Download";
|
|
2148
|
+
return /* @__PURE__ */ React.createElement("div", { style: { position: "fixed", right: 72, bottom: 18, zIndex: 90, width: 360, maxWidth: "calc(100vw - 96px)", display: "flex", flexDirection: "column", gap: 10 } }, items.map((it) => {
|
|
2117
2149
|
const peer = (peers || []).find((p) => p.id === it.peerId || p.userId === it.peerId) || {};
|
|
2118
2150
|
const who = peer.alias || peer.name || shortKey(it.peerId, 8, 5);
|
|
2119
|
-
return /* @__PURE__ */ React.createElement("div", { key: it.id, style: { border: "1px solid var(--line)", background: "var(--panel)", borderRadius: 12, padding: 12, boxShadow: "0 14px 40px rgba(0,0,0,0.35)" } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)", marginBottom: 6 } }, T && T.receivedFile || "Received file", " \xB7 ", who), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 13, fontWeight: 700, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, it.name), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)", marginTop: 3 } }, dkFileSize(it.size)), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 10 } }, /* @__PURE__ */ React.createElement("button", { onClick: () => ctl.dismissIncoming(it.id), style: { height: 30, borderRadius: 8, border: "1px solid var(--line)", background: "transparent", color: "var(--dim)", cursor: "pointer" } },
|
|
2151
|
+
return /* @__PURE__ */ React.createElement("div", { key: it.id, style: { border: "1px solid var(--line)", background: "var(--panel)", borderRadius: 12, padding: 12, boxShadow: "0 14px 40px rgba(0,0,0,0.35)" } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)", marginBottom: 6 } }, T && T.receivedFile || "Received file", " \xB7 ", who), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 13, fontWeight: 700, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, it.name), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)", marginTop: 3 } }, dkFileSize(it.size)), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 10, paddingRight: 2 } }, /* @__PURE__ */ React.createElement("button", { onClick: () => ctl.dismissIncoming(it.id), style: { height: 30, minWidth: 58, borderRadius: 8, border: "1px solid var(--line)", background: "transparent", color: "var(--dim)", cursor: "pointer" } }, closeLabel), /* @__PURE__ */ React.createElement("a", { href: it.url, download: it.name, onClick: () => setTimeout(() => ctl.dismissIncoming(it.id), 500), style: { height: 30, minWidth: 76, textAlign: "center", borderRadius: 8, padding: "6px 10px", background: "var(--accent)", color: "#fff", textDecoration: "none", fontFamily: "var(--mono)", fontSize: 12 } }, downloadLabel)));
|
|
2120
2152
|
}));
|
|
2121
2153
|
}
|
|
2122
2154
|
Object.assign(window, { useRtcFileController, RtcFileInbox });
|
|
@@ -2451,6 +2483,8 @@ const STR = {
|
|
|
2451
2483
|
block: "Block peer",
|
|
2452
2484
|
remove: "Remove friend",
|
|
2453
2485
|
receivedFile: "Received file",
|
|
2486
|
+
receivedClose: "Close",
|
|
2487
|
+
downloadFile: "Download",
|
|
2454
2488
|
attach: "Attach file",
|
|
2455
2489
|
message: "Message",
|
|
2456
2490
|
send: "Send",
|
|
@@ -2543,6 +2577,8 @@ const STR = {
|
|
|
2543
2577
|
block: "\u62C9\u9ED1",
|
|
2544
2578
|
remove: "\u5220\u9664\u597D\u53CB",
|
|
2545
2579
|
receivedFile: "\u6536\u5230\u6587\u4EF6",
|
|
2580
|
+
receivedClose: "\u5173\u95ED",
|
|
2581
|
+
downloadFile: "\u4E0B\u8F7D",
|
|
2546
2582
|
attach: "\u9644\u4EF6",
|
|
2547
2583
|
message: "\u6D88\u606F",
|
|
2548
2584
|
send: "\u53D1\u9001",
|
|
@@ -2682,9 +2718,9 @@ function DkApp() {
|
|
|
2682
2718
|
if (!activeId || !text || !text.trim()) return;
|
|
2683
2719
|
dkApi.send(activeId, text.trim()).then(() => data.loadThread(activeId)).then(data.refresh);
|
|
2684
2720
|
};
|
|
2685
|
-
const onSendFile = (file) => {
|
|
2721
|
+
const onSendFile = (file, onProgress) => {
|
|
2686
2722
|
if (!activeId || !file) return Promise.resolve({ ok: false });
|
|
2687
|
-
return rtcFileCtl.sendFile(activeId, file).then((r) => {
|
|
2723
|
+
return rtcFileCtl.sendFile(activeId, file, onProgress).then((r) => {
|
|
2688
2724
|
if (r && r.ok && r.via === "webrtc") {
|
|
2689
2725
|
return dkApi.send(activeId, (t.lang === "zh" ? "\u5DF2\u901A\u8FC7 WebRTC \u53D1\u9001\u6587\u4EF6: " : "sent file via WebRTC: ") + file.name).then(() => ({ ok: true, via: "webrtc" }));
|
|
2690
2726
|
}
|
|
@@ -2695,9 +2731,9 @@ function DkApp() {
|
|
|
2695
2731
|
return r;
|
|
2696
2732
|
});
|
|
2697
2733
|
};
|
|
2698
|
-
const onSendRtcFile = (file) => {
|
|
2734
|
+
const onSendRtcFile = (file, onProgress) => {
|
|
2699
2735
|
if (!activeId || !file) return Promise.resolve({ ok: false, error: "no active peer or file" });
|
|
2700
|
-
return rtcFileCtl.sendFile(activeId, file).then((r) => {
|
|
2736
|
+
return rtcFileCtl.sendFile(activeId, file, onProgress).then((r) => {
|
|
2701
2737
|
if (r && r.ok && r.via === "webrtc") {
|
|
2702
2738
|
return dkApi.send(activeId, (t.lang === "zh" ? "\u5DF2\u901A\u8FC7 WebRTC \u53D1\u9001\u6587\u4EF6: " : "sent file via WebRTC: ") + file.name).then(() => ({ ok: true, via: "webrtc" }));
|
|
2703
2739
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/lan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.224",
|
|
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",
|