@decentnetwork/lan 0.1.220 → 0.1.222
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 +58 -18
- package/package.json +1 -1
package/dist/ui/desktop/app.js
CHANGED
|
@@ -1365,10 +1365,11 @@ function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, selMode, sele
|
|
|
1365
1365
|
), mine && m.status === "queued" ? /* @__PURE__ */ React.createElement("span", { style: { display: "inline-flex", alignItems: "center", gap: 2 }, title: "waiting for peer \u2014 will send when they're online" }, /* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 11, stroke: 2.2, color: "var(--faint)" }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10, color: "var(--faint)" } }, T.queued || "queued")) : mine && m.status && /* @__PURE__ */ React.createElement(Icon, { name: "checkCheck", size: 12, stroke: 2.2, color: m.status === "read" ? "var(--accent)" : "var(--faint)" })))
|
|
1366
1366
|
);
|
|
1367
1367
|
}
|
|
1368
|
-
function Conversation({ T, peer, lang, thread: threadProp, onSend, onSendFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread }) {
|
|
1368
|
+
function Conversation({ T, peer, lang, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread }) {
|
|
1369
1369
|
const scrollRef = React.useRef(null);
|
|
1370
1370
|
const followScrollRef = React.useRef(true);
|
|
1371
1371
|
const fileRef = React.useRef(null);
|
|
1372
|
+
const rtcFileRef = React.useRef(null);
|
|
1372
1373
|
const [menu, setMenu] = React.useState(false);
|
|
1373
1374
|
const [selMode, setSelMode] = React.useState(false);
|
|
1374
1375
|
const [sel, setSel] = React.useState(() => /* @__PURE__ */ new Set());
|
|
@@ -1541,12 +1542,13 @@ function Conversation({ T, peer, lang, thread: threadProp, onSend, onSendFile, o
|
|
|
1541
1542
|
requestAnimationFrame(scrollToBottom);
|
|
1542
1543
|
}
|
|
1543
1544
|
};
|
|
1544
|
-
const sendFiles = (files) => {
|
|
1545
|
-
|
|
1545
|
+
const sendFiles = (files, mode) => {
|
|
1546
|
+
const sender = mode === "webrtc" ? onSendRtcFile : onSendFile;
|
|
1547
|
+
if (!files || !files.length || !sender) return;
|
|
1546
1548
|
const file = files[0];
|
|
1547
1549
|
followScrollRef.current = true;
|
|
1548
|
-
setSending({ name: file.name });
|
|
1549
|
-
Promise.resolve(
|
|
1550
|
+
setSending({ name: file.name, via: mode });
|
|
1551
|
+
Promise.resolve(sender(file)).then((r) => {
|
|
1550
1552
|
if (r && r.ok === false) window.alert((lang === "zh" ? "\u53D1\u9001\u5931\u8D25: " : "Send failed: ") + (r.error || ""));
|
|
1551
1553
|
}).finally(() => {
|
|
1552
1554
|
setSending(null);
|
|
@@ -1572,7 +1574,18 @@ function Conversation({ T, peer, lang, thread: threadProp, onSend, onSendFile, o
|
|
|
1572
1574
|
onDrop
|
|
1573
1575
|
},
|
|
1574
1576
|
dragOver && /* @__PURE__ */ React.createElement("div", { style: { position: "absolute", inset: 0, zIndex: 30, background: "rgba(91,140,255,0.10)", border: "2px dashed var(--accent)", borderRadius: 12, display: "flex", alignItems: "center", justifyContent: "center", pointerEvents: "none", fontFamily: "var(--mono)", fontSize: 14, color: "var(--accent)" } }, lang === "zh" ? "\u677E\u5F00\u53D1\u9001\u6587\u4EF6" : "Drop to send file"),
|
|
1575
|
-
/* @__PURE__ */ React.createElement("div", { style: { height: 60, flexShrink: 0, borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", gap: 12, padding: "0 16px", background: "var(--panel)" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer, size: 34, radius: 8 }), /* @__PURE__ */ React.createElement("div", { style: { minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: peer.alias ? "var(--ui)" : "var(--mono)", fontSize: 15, fontWeight: 700, color: "var(--text)" } }, peer.alias || shortKey(peer.userId, 10, 6)), peer.agent && /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, "agent"), /* @__PURE__ */ React.createElement(RouteTag, { peer })), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 2 } }, /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.userId, title: peer.userId }, shortKey(peer.userId, 10, 6)), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement("button", { onClick: () => onOpenNet(peer), style: { background: "none", border: "none", cursor: "pointer", padding: 0, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--accent)", display: "inline-flex", alignItems: "center", gap: 4 } }, /* @__PURE__ */ React.createElement(Icon, { name: "network", size: 12, stroke: 2 }), " ", peer.ip))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), onCall && /* @__PURE__ */ React.createElement(Btn, { icon: "phone", title: T && T.audioCall || "Audio call", onClick: () => onCall(peer.userId, false) }), onCall && /* @__PURE__ */ React.createElement(Btn, { icon: "video", title: T && T.videoCall || "Video call", onClick: () => onCall(peer.userId, true) }),
|
|
1577
|
+
/* @__PURE__ */ React.createElement("div", { style: { height: 60, flexShrink: 0, borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", gap: 12, padding: "0 16px", background: "var(--panel)" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer, size: 34, radius: 8 }), /* @__PURE__ */ React.createElement("div", { style: { minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: peer.alias ? "var(--ui)" : "var(--mono)", fontSize: 15, fontWeight: 700, color: "var(--text)" } }, peer.alias || shortKey(peer.userId, 10, 6)), peer.agent && /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, "agent"), /* @__PURE__ */ React.createElement(RouteTag, { peer })), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 2 } }, /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.userId, title: peer.userId }, shortKey(peer.userId, 10, 6)), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement("button", { onClick: () => onOpenNet(peer), style: { background: "none", border: "none", cursor: "pointer", padding: 0, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--accent)", display: "inline-flex", alignItems: "center", gap: 4 } }, /* @__PURE__ */ React.createElement(Icon, { name: "network", size: 12, stroke: 2 }), " ", peer.ip))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), onCall && /* @__PURE__ */ React.createElement(Btn, { icon: "phone", title: T && T.audioCall || "Audio call", onClick: () => onCall(peer.userId, false) }), onCall && /* @__PURE__ */ React.createElement(Btn, { icon: "video", title: T && T.videoCall || "Video call", onClick: () => onCall(peer.userId, true) }), onSendRtcFile && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
1578
|
+
"input",
|
|
1579
|
+
{
|
|
1580
|
+
ref: rtcFileRef,
|
|
1581
|
+
type: "file",
|
|
1582
|
+
style: { display: "none" },
|
|
1583
|
+
onChange: (e) => {
|
|
1584
|
+
sendFiles(e.target.files, "webrtc");
|
|
1585
|
+
e.target.value = "";
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
), /* @__PURE__ */ React.createElement(Btn, { icon: "file", title: T && T.sendWebrtcFile || "Send file via WebRTC", onClick: () => rtcFileRef.current && rtcFileRef.current.click() })), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(Btn, { icon: "more", onClick: () => setMenu((v) => !v) }), menu && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { onClick: () => setMenu(false), style: { position: "fixed", inset: 0, zIndex: 40 } }), /* @__PURE__ */ React.createElement("div", { style: { position: "absolute", right: 0, top: 36, zIndex: 50, width: 180, background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 9, padding: 6, boxShadow: "0 14px 40px rgba(0,0,0,0.4)" } }, /* @__PURE__ */ React.createElement(MenuItem, { icon: "hash", label: T.alias, onClick: () => {
|
|
1576
1589
|
setMenu(false);
|
|
1577
1590
|
onAlias(peer);
|
|
1578
1591
|
} }), /* @__PURE__ */ React.createElement(MenuItem, { icon: "check", label: T.selectDelete || "Select & delete", onClick: () => {
|
|
@@ -1620,7 +1633,7 @@ function Conversation({ T, peer, lang, thread: threadProp, onSend, onSendFile, o
|
|
|
1620
1633
|
stroke: 2.2,
|
|
1621
1634
|
color: flash.kind === "err" ? "var(--danger)" : "var(--accent)"
|
|
1622
1635
|
}
|
|
1623
|
-
), 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.name), /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto", display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React.createElement(
|
|
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(
|
|
1624
1637
|
"input",
|
|
1625
1638
|
{
|
|
1626
1639
|
ref: fileRef,
|
|
@@ -1715,9 +1728,9 @@ function MenuItem({ icon, label, onClick, danger }) {
|
|
|
1715
1728
|
function ChatEmpty({ T }) {
|
|
1716
1729
|
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 12, color: "var(--faint)", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "message", size: 40, stroke: 1.4, color: "var(--line)" }), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13 } }, T.pickPeer));
|
|
1717
1730
|
}
|
|
1718
|
-
function ChatTab({ T, lang, peers, requests, activeId, thread, onSelect, onAct, onAdd, onSend, onSendFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread }) {
|
|
1731
|
+
function ChatTab({ T, lang, peers, requests, activeId, thread, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread }) {
|
|
1719
1732
|
const peer = peers.find((p) => p.id === activeId);
|
|
1720
|
-
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", minWidth: 0, minHeight: 0 } }, /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, thread, onSend, onSendFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
|
|
1733
|
+
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", minWidth: 0, minHeight: 0 } }, /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
|
|
1721
1734
|
}
|
|
1722
1735
|
Object.assign(window, { ChatTab });
|
|
1723
1736
|
function StatTile({ label, value, sub, tone }) {
|
|
@@ -1835,8 +1848,8 @@ function ProfileTab({ T, me, onEdit }) {
|
|
|
1835
1848
|
}
|
|
1836
1849
|
Object.assign(window, { ProfileTab });
|
|
1837
1850
|
const DK_FILE_RTC_KIND = "file";
|
|
1838
|
-
const DK_FILE_RTC_CHUNK =
|
|
1839
|
-
const DK_FILE_RTC_OPEN_TIMEOUT_MS =
|
|
1851
|
+
const DK_FILE_RTC_CHUNK = 16 * 1024;
|
|
1852
|
+
const DK_FILE_RTC_OPEN_TIMEOUT_MS = 2e4;
|
|
1840
1853
|
const DK_FILE_RTC_SIGNAL_TTL_MS = 10 * 60 * 1e3;
|
|
1841
1854
|
function dkFileRtcId() {
|
|
1842
1855
|
if (window.crypto && window.crypto.randomUUID) return window.crypto.randomUUID();
|
|
@@ -1845,12 +1858,17 @@ function dkFileRtcId() {
|
|
|
1845
1858
|
function dkFileRtcEnvelope(fileId, type, extra) {
|
|
1846
1859
|
return Object.assign({ dkRtc: 1, kind: DK_FILE_RTC_KIND, fileId, type, ts: Date.now() }, extra || {});
|
|
1847
1860
|
}
|
|
1848
|
-
function
|
|
1861
|
+
function dkRtcState(pc) {
|
|
1862
|
+
if (!pc) return "";
|
|
1863
|
+
return "ice=" + pc.iceConnectionState + " conn=" + pc.connectionState + " gather=" + pc.iceGatheringState;
|
|
1864
|
+
}
|
|
1865
|
+
function dkWaitForOpen(dc, timeoutMs, describeState) {
|
|
1849
1866
|
if (dc.readyState === "open") return Promise.resolve();
|
|
1850
1867
|
return new Promise((resolve, reject) => {
|
|
1851
1868
|
const timer = setTimeout(() => {
|
|
1852
1869
|
cleanup();
|
|
1853
|
-
|
|
1870
|
+
const s = describeState ? describeState() : "";
|
|
1871
|
+
reject(new Error("WebRTC data channel open timed out" + (s ? " (" + s + ")" : "")));
|
|
1854
1872
|
}, timeoutMs);
|
|
1855
1873
|
const cleanup = () => {
|
|
1856
1874
|
clearTimeout(timer);
|
|
@@ -1864,11 +1882,13 @@ function dkWaitForOpen(dc, timeoutMs) {
|
|
|
1864
1882
|
};
|
|
1865
1883
|
const onError = () => {
|
|
1866
1884
|
cleanup();
|
|
1867
|
-
|
|
1885
|
+
const s = describeState ? describeState() : "";
|
|
1886
|
+
reject(new Error("WebRTC data channel failed" + (s ? " (" + s + ")" : "")));
|
|
1868
1887
|
};
|
|
1869
1888
|
const onClose = () => {
|
|
1870
1889
|
cleanup();
|
|
1871
|
-
|
|
1890
|
+
const s = describeState ? describeState() : "";
|
|
1891
|
+
reject(new Error("WebRTC data channel closed before open" + (s ? " (" + s + ")" : "")));
|
|
1872
1892
|
};
|
|
1873
1893
|
dc.addEventListener("open", onOpen);
|
|
1874
1894
|
dc.addEventListener("error", onError);
|
|
@@ -1960,6 +1980,8 @@ function useRtcFileController(selfId, onReceivedText) {
|
|
|
1960
1980
|
if (env.type === "offer") {
|
|
1961
1981
|
if (sess) cleanup(fileId);
|
|
1962
1982
|
const pc = new RTCPeerConnection({ iceServers: CALL_ICE_SERVERS });
|
|
1983
|
+
pc.oniceconnectionstatechange = () => console.warn("[file-rtc] receiver ice=" + pc.iceConnectionState + " conn=" + pc.connectionState);
|
|
1984
|
+
pc.onconnectionstatechange = () => console.warn("[file-rtc] receiver conn=" + pc.connectionState + " ice=" + pc.iceConnectionState);
|
|
1963
1985
|
pc.onicecandidate = (ev) => {
|
|
1964
1986
|
if (ev.candidate) sendSignal(peerId, fileId, "candidate", { candidate: ev.candidate.toJSON() }).catch(() => {
|
|
1965
1987
|
});
|
|
@@ -2035,6 +2057,8 @@ function useRtcFileController(selfId, onReceivedText) {
|
|
|
2035
2057
|
};
|
|
2036
2058
|
const sendSignal = (type, extra) => bus.send(peerId, dkFileRtcEnvelope(fileId, type, extra));
|
|
2037
2059
|
try {
|
|
2060
|
+
pc.oniceconnectionstatechange = () => console.warn("[file-rtc] sender ice=" + pc.iceConnectionState + " conn=" + pc.connectionState);
|
|
2061
|
+
pc.onconnectionstatechange = () => console.warn("[file-rtc] sender conn=" + pc.connectionState + " ice=" + pc.iceConnectionState);
|
|
2038
2062
|
pc.onicecandidate = (ev) => {
|
|
2039
2063
|
if (ev.candidate) sendSignal("candidate", { candidate: ev.candidate.toJSON() }).catch(() => {
|
|
2040
2064
|
});
|
|
@@ -2045,7 +2069,7 @@ function useRtcFileController(selfId, onReceivedText) {
|
|
|
2045
2069
|
sdp: offer.sdp,
|
|
2046
2070
|
meta: { name: file.name, size: file.size, mime: file.type || "application/octet-stream" }
|
|
2047
2071
|
});
|
|
2048
|
-
await dkWaitForOpen(dc, DK_FILE_RTC_OPEN_TIMEOUT_MS);
|
|
2072
|
+
await dkWaitForOpen(dc, DK_FILE_RTC_OPEN_TIMEOUT_MS, () => dkRtcState(pc));
|
|
2049
2073
|
dc.send(JSON.stringify({ type: "meta", name: file.name, size: file.size, mime: file.type || "application/octet-stream" }));
|
|
2050
2074
|
let sent = 0;
|
|
2051
2075
|
const highWater = 4 * 1024 * 1024;
|
|
@@ -2091,7 +2115,8 @@ function RtcFileInbox({ T, peers, ctl }) {
|
|
|
2091
2115
|
Object.assign(window, { useRtcFileController, RtcFileInbox });
|
|
2092
2116
|
const CALL_ICE_SERVERS = [
|
|
2093
2117
|
{ urls: "stun:stun.l.google.com:19302" },
|
|
2094
|
-
{ urls: "turn:tokyo.fi.chat:3478", username: "allcom", credential: "allcompass" }
|
|
2118
|
+
{ urls: "turn:tokyo.fi.chat:3478", username: "allcom", credential: "allcompass" },
|
|
2119
|
+
{ urls: "turn:tokyo.fi.chat:3478?transport=tcp", username: "allcom", credential: "allcompass" }
|
|
2095
2120
|
];
|
|
2096
2121
|
function useCallController(selfId) {
|
|
2097
2122
|
const [incoming, setIncoming] = React.useState(null);
|
|
@@ -2415,6 +2440,7 @@ const STR = {
|
|
|
2415
2440
|
call: "Voice call",
|
|
2416
2441
|
alias: "Set alias",
|
|
2417
2442
|
sendFile: "Send file",
|
|
2443
|
+
sendWebrtcFile: "Send file via WebRTC",
|
|
2418
2444
|
block: "Block peer",
|
|
2419
2445
|
remove: "Remove friend",
|
|
2420
2446
|
receivedFile: "Received file",
|
|
@@ -2506,6 +2532,7 @@ const STR = {
|
|
|
2506
2532
|
call: "\u8BED\u97F3\u901A\u8BDD",
|
|
2507
2533
|
alias: "\u8BBE\u7F6E\u5907\u6CE8",
|
|
2508
2534
|
sendFile: "\u53D1\u9001\u6587\u4EF6",
|
|
2535
|
+
sendWebrtcFile: "\u901A\u8FC7 WebRTC \u53D1\u9001\u6587\u4EF6",
|
|
2509
2536
|
block: "\u62C9\u9ED1",
|
|
2510
2537
|
remove: "\u5220\u9664\u597D\u53CB",
|
|
2511
2538
|
receivedFile: "\u6536\u5230\u6587\u4EF6",
|
|
@@ -2661,6 +2688,19 @@ function DkApp() {
|
|
|
2661
2688
|
return r;
|
|
2662
2689
|
});
|
|
2663
2690
|
};
|
|
2691
|
+
const onSendRtcFile = (file) => {
|
|
2692
|
+
if (!activeId || !file) return Promise.resolve({ ok: false, error: "no active peer or file" });
|
|
2693
|
+
return rtcFileCtl.sendFile(activeId, file).then((r) => {
|
|
2694
|
+
if (r && r.ok && r.via === "webrtc") {
|
|
2695
|
+
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" }));
|
|
2696
|
+
}
|
|
2697
|
+
return { ok: false, error: r && r.error || "WebRTC file send failed" };
|
|
2698
|
+
}).then((r) => {
|
|
2699
|
+
data.loadThread(activeId);
|
|
2700
|
+
data.refresh();
|
|
2701
|
+
return r;
|
|
2702
|
+
});
|
|
2703
|
+
};
|
|
2664
2704
|
const onAlias = (peer) => {
|
|
2665
2705
|
const a = window.prompt("Set alias for this peer (empty to clear):", peer.alias || "");
|
|
2666
2706
|
if (a !== null) dkApi.alias(peer.id, a).then(data.refresh);
|
|
@@ -2678,7 +2718,7 @@ function DkApp() {
|
|
|
2678
2718
|
{ id: "network", icon: "network", label: T.network },
|
|
2679
2719
|
{ id: "profile", icon: "userRound", label: T.profile }
|
|
2680
2720
|
];
|
|
2681
|
-
return /* @__PURE__ */ React.createElement("div", { style: { ...vars, "--row-pad": rowPad, position: "fixed", inset: 0, display: "flex", background: "var(--bg)", color: "var(--text)", fontFamily: "var(--ui)" } }, /* @__PURE__ */ React.createElement("div", { style: { width: 68, flexShrink: 0, borderRight: "1px solid var(--line)", background: "var(--rail)", display: "flex", flexDirection: "column", alignItems: "center", padding: "14px 0", gap: 8 } }, /* @__PURE__ */ React.createElement("div", { style: { width: 38, height: 38, borderRadius: 10, background: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: 8 } }, /* @__PURE__ */ React.createElement(Icon, { name: "terminal", size: 20, color: "#fff", stroke: 2.2 })), nav.map((n) => /* @__PURE__ */ React.createElement(RailBtn, { key: n.id, icon: n.icon, label: n.label, active: tab === n.id, soon: n.soon, onClick: () => setTab(n.id) })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 36, radius: 9 }))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, minHeight: 0, display: "flex", flexDirection: "column" } }, /* @__PURE__ */ React.createElement("div", { style: { height: 46, flexShrink: 0, borderBottom: "1px solid var(--line)", background: "var(--panel)", display: "flex", alignItems: "center", gap: 12, padding: "0 16px" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 14, fontWeight: 700, letterSpacing: -0.3, color: "var(--text)" } }, "beagle"), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)" } }, "\xB7 ", nav.find((n) => n.id === tab).label.toLowerCase()), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, me.channel, " \xB7 lan ", me.lanVer), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 7, padding: "0 4px" } }, /* @__PURE__ */ React.createElement(StatusDot, { online: me.online }), /* @__PURE__ */ React.createElement(Mono, { size: 12.5, copy: me.ip }, me.ip)), /* @__PURE__ */ React.createElement("span", { style: { width: 1, height: 22, background: "var(--line)" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 26, radius: 7 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, fontWeight: 600, color: "var(--text)" } }, me.name))), tab === "chat" && /* @__PURE__ */ React.createElement(ChatTab, { T, lang: t.lang, peers, requests, activeId, thread: data.threads[activeId], onSelect, onAct, onAdd, onSend, onSendFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread: () => activeId && data.loadThread(activeId) }), tab === "network" && /* @__PURE__ */ React.createElement(NetworkTab, { T, me, peers, exits, activeExit, reqCount: requests.length, onSetExit, onOpenChat }), tab === "profile" && /* @__PURE__ */ React.createElement(ProfileTab, { T, me, onEdit })), /* @__PURE__ */ React.createElement(TweaksPanel, null, /* @__PURE__ */ React.createElement(TweakSection, { label: t.lang === "zh" ? "\u5916\u89C2" : "Appearance" }), /* @__PURE__ */ React.createElement(
|
|
2721
|
+
return /* @__PURE__ */ React.createElement("div", { style: { ...vars, "--row-pad": rowPad, position: "fixed", inset: 0, display: "flex", background: "var(--bg)", color: "var(--text)", fontFamily: "var(--ui)" } }, /* @__PURE__ */ React.createElement("div", { style: { width: 68, flexShrink: 0, borderRight: "1px solid var(--line)", background: "var(--rail)", display: "flex", flexDirection: "column", alignItems: "center", padding: "14px 0", gap: 8 } }, /* @__PURE__ */ React.createElement("div", { style: { width: 38, height: 38, borderRadius: 10, background: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: 8 } }, /* @__PURE__ */ React.createElement(Icon, { name: "terminal", size: 20, color: "#fff", stroke: 2.2 })), nav.map((n) => /* @__PURE__ */ React.createElement(RailBtn, { key: n.id, icon: n.icon, label: n.label, active: tab === n.id, soon: n.soon, onClick: () => setTab(n.id) })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 36, radius: 9 }))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, minHeight: 0, display: "flex", flexDirection: "column" } }, /* @__PURE__ */ React.createElement("div", { style: { height: 46, flexShrink: 0, borderBottom: "1px solid var(--line)", background: "var(--panel)", display: "flex", alignItems: "center", gap: 12, padding: "0 16px" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 14, fontWeight: 700, letterSpacing: -0.3, color: "var(--text)" } }, "beagle"), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)" } }, "\xB7 ", nav.find((n) => n.id === tab).label.toLowerCase()), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, me.channel, " \xB7 lan ", me.lanVer), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 7, padding: "0 4px" } }, /* @__PURE__ */ React.createElement(StatusDot, { online: me.online }), /* @__PURE__ */ React.createElement(Mono, { size: 12.5, copy: me.ip }, me.ip)), /* @__PURE__ */ React.createElement("span", { style: { width: 1, height: 22, background: "var(--line)" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 26, radius: 7 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, fontWeight: 600, color: "var(--text)" } }, me.name))), tab === "chat" && /* @__PURE__ */ React.createElement(ChatTab, { T, lang: t.lang, peers, requests, activeId, thread: data.threads[activeId], onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread: () => activeId && data.loadThread(activeId) }), tab === "network" && /* @__PURE__ */ React.createElement(NetworkTab, { T, me, peers, exits, activeExit, reqCount: requests.length, onSetExit, onOpenChat }), tab === "profile" && /* @__PURE__ */ React.createElement(ProfileTab, { T, me, onEdit })), /* @__PURE__ */ React.createElement(TweaksPanel, null, /* @__PURE__ */ React.createElement(TweakSection, { label: t.lang === "zh" ? "\u5916\u89C2" : "Appearance" }), /* @__PURE__ */ React.createElement(
|
|
2682
2722
|
TweakRadio,
|
|
2683
2723
|
{
|
|
2684
2724
|
label: t.lang === "zh" ? "\u4E3B\u9898" : "Theme",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/lan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.222",
|
|
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",
|