@decentnetwork/lan 0.1.225 → 0.1.227
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 +69 -3
- package/package.json +1 -1
package/dist/ui/desktop/app.js
CHANGED
|
@@ -1181,8 +1181,18 @@ const mediaBtnStyle = {
|
|
|
1181
1181
|
justifyContent: "center",
|
|
1182
1182
|
lineHeight: 0
|
|
1183
1183
|
};
|
|
1184
|
+
function dkRtcFileFromText(peerId, text) {
|
|
1185
|
+
const m = /^received file via WebRTC: (.+) \(([^)]+)\)$/.exec(String(text || ""));
|
|
1186
|
+
if (!m || !window.__dkRtcFiles) return null;
|
|
1187
|
+
const name = m[1];
|
|
1188
|
+
for (const item of window.__dkRtcFiles.values()) {
|
|
1189
|
+
if (item && item.peerId === peerId && item.name === name) return item;
|
|
1190
|
+
}
|
|
1191
|
+
return null;
|
|
1192
|
+
}
|
|
1184
1193
|
function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, selMode, selected, onToggleSel, busy }) {
|
|
1185
1194
|
const mine = m.from === "me";
|
|
1195
|
+
const rtcFile = !mine && !m.file ? dkRtcFileFromText(peer.userId || peer.id, m.text) : null;
|
|
1186
1196
|
return /* @__PURE__ */ React.createElement(
|
|
1187
1197
|
"div",
|
|
1188
1198
|
{
|
|
@@ -1212,7 +1222,52 @@ function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, selMode, sele
|
|
|
1212
1222
|
justifyContent: "center"
|
|
1213
1223
|
} }, selected && /* @__PURE__ */ React.createElement(Icon, { name: "check", size: 12, stroke: 3, color: "#fff" })),
|
|
1214
1224
|
!mine && /* @__PURE__ */ React.createElement(DkAvatar, { peer, size: 24, radius: 6, dot: false }),
|
|
1215
|
-
/* @__PURE__ */ React.createElement("div", { style: { maxWidth: m.file && (m.file.media === "image" || m.file.media === "video") ? "min(680px, 88%)" : "64%", display: "flex", flexDirection: "column", alignItems: mine ? "flex-end" : "flex-start" } },
|
|
1225
|
+
/* @__PURE__ */ React.createElement("div", { style: { maxWidth: m.file && (m.file.media === "image" || m.file.media === "video") || rtcFile && (rtcFile.media === "image" || rtcFile.media === "video") ? "min(680px, 88%)" : "64%", display: "flex", flexDirection: "column", alignItems: mine ? "flex-end" : "flex-start" } }, rtcFile ? /* @__PURE__ */ React.createElement("div", { style: {
|
|
1226
|
+
display: "flex",
|
|
1227
|
+
flexDirection: "column",
|
|
1228
|
+
gap: 6,
|
|
1229
|
+
maxWidth: rtcFile.media === "image" || rtcFile.media === "video" ? "100%" : 300,
|
|
1230
|
+
width: rtcFile.media === "image" || rtcFile.media === "video" ? "100%" : void 0,
|
|
1231
|
+
background: "var(--bub-them)",
|
|
1232
|
+
border: "1px solid var(--line)",
|
|
1233
|
+
borderRadius: 12,
|
|
1234
|
+
padding: 6
|
|
1235
|
+
} }, (rtcFile.media === "image" || rtcFile.media === "video") && /* @__PURE__ */ React.createElement("div", { "data-media": true, style: { position: "relative", lineHeight: 0 } }, rtcFile.media === "image" ? /* @__PURE__ */ React.createElement(
|
|
1236
|
+
"img",
|
|
1237
|
+
{
|
|
1238
|
+
src: rtcFile.url,
|
|
1239
|
+
alt: rtcFile.name,
|
|
1240
|
+
style: { display: "block", width: "100%", maxHeight: "72vh", borderRadius: 8, objectFit: "contain" }
|
|
1241
|
+
}
|
|
1242
|
+
) : /* @__PURE__ */ React.createElement(
|
|
1243
|
+
"video",
|
|
1244
|
+
{
|
|
1245
|
+
src: rtcFile.url,
|
|
1246
|
+
controls: true,
|
|
1247
|
+
preload: "metadata",
|
|
1248
|
+
style: { display: "block", width: "100%", maxHeight: "72vh", borderRadius: 8, objectFit: "contain", background: "#000" }
|
|
1249
|
+
}
|
|
1250
|
+
), /* @__PURE__ */ React.createElement("div", { style: { position: "absolute", top: 8, right: 8, display: "flex", gap: 6 } }, /* @__PURE__ */ React.createElement(
|
|
1251
|
+
"button",
|
|
1252
|
+
{
|
|
1253
|
+
onClick: () => onTheater({ kind: rtcFile.media, url: rtcFile.url, name: rtcFile.name }),
|
|
1254
|
+
title: "theater / \u7F51\u9875\u5168\u5C4F",
|
|
1255
|
+
style: mediaBtnStyle
|
|
1256
|
+
},
|
|
1257
|
+
/* @__PURE__ */ React.createElement(Icon, { name: "theater", size: 16, stroke: 2, color: "#fff" })
|
|
1258
|
+
), /* @__PURE__ */ React.createElement(
|
|
1259
|
+
"button",
|
|
1260
|
+
{
|
|
1261
|
+
onClick: (e) => {
|
|
1262
|
+
const w = e.currentTarget.closest("[data-media]");
|
|
1263
|
+
const el = w && w.querySelector("video,img");
|
|
1264
|
+
if (el && el.requestFullscreen) el.requestFullscreen();
|
|
1265
|
+
},
|
|
1266
|
+
title: "fullscreen / \u5168\u5C4F",
|
|
1267
|
+
style: mediaBtnStyle
|
|
1268
|
+
},
|
|
1269
|
+
/* @__PURE__ */ React.createElement(Icon, { name: "maximize", size: 16, stroke: 2, color: "#fff" })
|
|
1270
|
+
))), rtcFile.media === "audio" && /* @__PURE__ */ React.createElement("audio", { src: rtcFile.url, controls: true, style: { width: "100%" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "0 2px" } }, /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } }, rtcFile.name), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)", flexShrink: 0 } }, dkFileSize(rtcFile.size)), /* @__PURE__ */ React.createElement("a", { href: rtcFile.url, download: rtcFile.name, title: "download", style: { display: "inline-flex", flexShrink: 0 } }, /* @__PURE__ */ React.createElement(Icon, { name: "download", size: 15, stroke: 2, color: "var(--accent)" })))) : m.file ? (
|
|
1216
1271
|
// Inline preview/player. Received media: as soon as it's saved.
|
|
1217
1272
|
// Sent media: once it's fully delivered ('sent') — the daemon keeps a
|
|
1218
1273
|
// local copy so it plays on the sender's side too.
|
|
@@ -1947,9 +2002,19 @@ function dkFileDownload(blob, name) {
|
|
|
1947
2002
|
a.textContent = name || "agentnet-file";
|
|
1948
2003
|
return { url, name: a.download };
|
|
1949
2004
|
}
|
|
2005
|
+
function dkRememberRtcFile(peerId, fileId, name, size, url) {
|
|
2006
|
+
if (!window.__dkRtcFiles) window.__dkRtcFiles = /* @__PURE__ */ new Map();
|
|
2007
|
+
const item = { id: fileId, peerId, name, size, url, media: dkFileMediaKind(name) };
|
|
2008
|
+
window.__dkRtcFiles.set(peerId + "|" + name + "|" + size, item);
|
|
2009
|
+
return item;
|
|
2010
|
+
}
|
|
1950
2011
|
function useRtcFileController(selfId, onReceivedText) {
|
|
1951
2012
|
const [incoming, setIncoming] = React.useState([]);
|
|
1952
2013
|
const peersRef = React.useRef(/* @__PURE__ */ new Map());
|
|
2014
|
+
const onReceivedTextRef = React.useRef(onReceivedText);
|
|
2015
|
+
React.useEffect(() => {
|
|
2016
|
+
onReceivedTextRef.current = onReceivedText;
|
|
2017
|
+
}, [onReceivedText]);
|
|
1953
2018
|
React.useEffect(() => {
|
|
1954
2019
|
if (!selfId || !window.RTCPeerConnection || !window.dkRtcSignalBus) return;
|
|
1955
2020
|
const bus = dkRtcSignalBus();
|
|
@@ -1988,8 +2053,9 @@ function useRtcFileController(selfId, onReceivedText) {
|
|
|
1988
2053
|
const meta = sess.meta || {};
|
|
1989
2054
|
const blob = new Blob(sess.chunks, { type: meta.mime || "application/octet-stream" });
|
|
1990
2055
|
const dl = dkFileDownload(blob, meta.name || "agentnet-file");
|
|
2056
|
+
dkRememberRtcFile(peerId, fileId, dl.name, blob.size, dl.url);
|
|
1991
2057
|
setIncoming((arr) => [{ id: fileId, peerId, name: dl.name, size: blob.size, url: dl.url }].concat(arr).slice(0, 8));
|
|
1992
|
-
if (
|
|
2058
|
+
if (onReceivedTextRef.current) onReceivedTextRef.current(peerId, "received file via WebRTC: " + dl.name + " (" + dkFileSize(blob.size) + ")");
|
|
1993
2059
|
cleanup(fileId);
|
|
1994
2060
|
} else if (msg.type === "cancel") {
|
|
1995
2061
|
cleanup(fileId);
|
|
@@ -2071,7 +2137,7 @@ function useRtcFileController(selfId, onReceivedText) {
|
|
|
2071
2137
|
off();
|
|
2072
2138
|
for (const id of Array.from(peersRef.current.keys())) cleanup(id);
|
|
2073
2139
|
};
|
|
2074
|
-
}, [selfId
|
|
2140
|
+
}, [selfId]);
|
|
2075
2141
|
const sendFile = React.useCallback(async (peerId, file, onProgress) => {
|
|
2076
2142
|
if (!peerId || !file || !window.RTCPeerConnection || !window.dkRtcSignalBus) {
|
|
2077
2143
|
return { ok: false, fallback: true, error: "WebRTC unavailable" };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/lan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.227",
|
|
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",
|