@decentnetwork/beagle 0.1.36 → 0.1.38
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/desktop/app.js +96 -28
- package/dist/server.js +30 -7
- package/package.json +1 -1
package/dist/desktop/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
window.__DK_UI_VERSION="0.1.
|
|
1
|
+
window.__DK_UI_VERSION="0.1.37";
|
|
2
2
|
const ICON_PATHS = {
|
|
3
3
|
// ---- tab bar (the four must feel like one set) ----
|
|
4
4
|
users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
|
|
@@ -1622,7 +1622,7 @@ function DkGroupSender({ grp, T, avatar }) {
|
|
|
1622
1622
|
title: grp.sender,
|
|
1623
1623
|
style: { display: "inline-flex", alignItems: "center", gap: 5, background: "none", border: "none", padding: 0, cursor: "pointer" }
|
|
1624
1624
|
},
|
|
1625
|
-
avatar ? /* @__PURE__ */ React.createElement(
|
|
1625
|
+
avatar ? /* @__PURE__ */ React.createElement(DkEnsAvatar, { userid: grp.senderId, fallbackSeed: seed, size: 24, radius: 6 }) : /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, color: "var(--faint)" } }, grp.sender)
|
|
1626
1626
|
), open && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { onClick: () => setOpen(false), style: { position: "fixed", inset: 0, zIndex: 40 } }), /* @__PURE__ */ React.createElement("span", { style: {
|
|
1627
1627
|
position: "absolute",
|
|
1628
1628
|
left: 0,
|
|
@@ -1637,7 +1637,7 @@ function DkGroupSender({ grp, T, avatar }) {
|
|
|
1637
1637
|
display: "flex",
|
|
1638
1638
|
flexDirection: "column",
|
|
1639
1639
|
gap: 8
|
|
1640
|
-
} }, /* @__PURE__ */ React.createElement("span", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React.createElement(
|
|
1640
|
+
} }, /* @__PURE__ */ React.createElement("span", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React.createElement(DkEnsAvatar, { userid: grp.senderId, fallbackSeed: seed, size: 34, radius: 8 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, fontWeight: 700, color: "var(--text)", wordBreak: "break-all" } }, grp.sender)), grp.senderId ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Mono, { size: 10.5, dim: true, copy: grp.senderId, title: grp.senderId }, shortKey(grp.senderId, 12, 8)), /* @__PURE__ */ React.createElement(
|
|
1641
1641
|
"button",
|
|
1642
1642
|
{
|
|
1643
1643
|
onClick: addFriend,
|
|
@@ -2056,33 +2056,68 @@ function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, onReveal, onC
|
|
|
2056
2056
|
const meta = /* @__PURE__ */ React.createElement("div", { style: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 12.5, fontWeight: 600, color: mine ? "#fff" : "var(--text)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } }, m.file.name), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, color: mine ? "rgba(255,255,255,0.7)" : "var(--faint)", marginTop: 1 } }, busy === "cancel" ? `${m.file.size} \xB7 ${T.cancelling || "Cancelling\u2026"}` : busy === "retry" ? `${m.file.size} \xB7 ${T.retrying || "Retrying\u2026"}` : m.file.status === "queued" ? `${m.file.size} \xB7 ${T.queued || "queued"}` : m.file.status === "sending" ? `${m.file.size} \xB7 ${dkPct(m.file.pct)}${m.file.kbps ? " \xB7 " + dkSpeed(m.file.kbps) : ""}` : m.file.status === "failed" ? `${m.file.size} \xB7 failed` : m.file.status === "cancelled" ? `${m.file.size} \xB7 ${T.cancelled || "cancelled"}` : mine ? `${m.file.size} \xB7 sent` : `${m.file.size} \xB7 ${T.open || "open"}`));
|
|
2057
2057
|
if (mine) {
|
|
2058
2058
|
const btnBusy = !!busy;
|
|
2059
|
-
return /* @__PURE__ */ React.createElement(
|
|
2060
|
-
"
|
|
2059
|
+
return /* @__PURE__ */ React.createElement(
|
|
2060
|
+
"div",
|
|
2061
2061
|
{
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
onClick: (
|
|
2065
|
-
e.stopPropagation();
|
|
2066
|
-
if (!btnBusy)
|
|
2067
|
-
onCancel(m.id);
|
|
2068
|
-
},
|
|
2069
|
-
style: { background: "rgba(255,255,255,0.16)", border: "none", borderRadius: 6, cursor: btnBusy ? "wait" : "pointer", padding: 3, marginLeft: 2, display: "inline-flex", flexShrink: 0, opacity: btnBusy ? 0.5 : 1 }
|
|
2062
|
+
style: { ...cardStyle, cursor: "pointer", opacity: btnBusy ? 0.85 : 1 },
|
|
2063
|
+
title: T.open || "open",
|
|
2064
|
+
onClick: () => window.open(dkFileUrl(m.file.name), "_blank", "noopener")
|
|
2070
2065
|
},
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2066
|
+
icon,
|
|
2067
|
+
meta,
|
|
2068
|
+
onReveal && /* @__PURE__ */ React.createElement(
|
|
2069
|
+
"button",
|
|
2070
|
+
{
|
|
2071
|
+
title: T.revealInFinder || "\u5728\u6587\u4EF6\u7BA1\u7406\u5668\u4E2D\u663E\u793A / Show in file manager",
|
|
2072
|
+
onClick: (e) => {
|
|
2073
|
+
e.stopPropagation();
|
|
2074
|
+
onReveal(m.file.name);
|
|
2075
|
+
},
|
|
2076
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: 0, display: "inline-flex", flexShrink: 0 }
|
|
2081
2077
|
},
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
/* @__PURE__ */ React.createElement(
|
|
2085
|
-
|
|
2078
|
+
/* @__PURE__ */ React.createElement(Icon, { name: "folderOpen", size: 15, stroke: 2, color: "rgba(255,255,255,0.85)" })
|
|
2079
|
+
),
|
|
2080
|
+
/* @__PURE__ */ React.createElement(
|
|
2081
|
+
"a",
|
|
2082
|
+
{
|
|
2083
|
+
href: dkFileDownloadUrl(m.file.name),
|
|
2084
|
+
download: m.file.name,
|
|
2085
|
+
title: "download",
|
|
2086
|
+
onClick: (e) => e.stopPropagation(),
|
|
2087
|
+
style: { display: "inline-flex", flexShrink: 0 }
|
|
2088
|
+
},
|
|
2089
|
+
/* @__PURE__ */ React.createElement(Icon, { name: "download", size: 15, stroke: 2, color: "rgba(255,255,255,0.85)" })
|
|
2090
|
+
),
|
|
2091
|
+
busy === "cancel" || busy === "retry" ? /* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 16, stroke: 2, color: "rgba(255,255,255,0.85)" }) : m.file.status === "queued" ? /* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 16, stroke: 2, color: "rgba(255,255,255,0.85)" }) : m.file.status === "sending" ? /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, color: "rgba(255,255,255,0.9)", whiteSpace: "nowrap" } }, dkPct(m.file.pct), m.file.kbps ? " \xB7 " + dkSpeed(m.file.kbps) : "") : m.file.status === "failed" || m.file.status === "cancelled" ? /* @__PURE__ */ React.createElement(Icon, { name: "x", size: 16, stroke: 2.4, color: "rgba(255,200,190,0.95)" }) : /* @__PURE__ */ React.createElement(Icon, { name: "checkCheck", size: 16, stroke: 2, color: "rgba(255,255,255,0.85)" }),
|
|
2092
|
+
(m.file.status === "sending" || m.file.status === "queued" || busy === "cancel") && m.id && /* @__PURE__ */ React.createElement(
|
|
2093
|
+
"button",
|
|
2094
|
+
{
|
|
2095
|
+
title: T.cancel || "cancel",
|
|
2096
|
+
disabled: btnBusy,
|
|
2097
|
+
onClick: (e) => {
|
|
2098
|
+
e.stopPropagation();
|
|
2099
|
+
if (!btnBusy)
|
|
2100
|
+
onCancel(m.id);
|
|
2101
|
+
},
|
|
2102
|
+
style: { background: "rgba(255,255,255,0.16)", border: "none", borderRadius: 6, cursor: btnBusy ? "wait" : "pointer", padding: 3, marginLeft: 2, display: "inline-flex", flexShrink: 0, opacity: btnBusy ? 0.5 : 1 }
|
|
2103
|
+
},
|
|
2104
|
+
/* @__PURE__ */ React.createElement(Icon, { name: "x", size: 13, stroke: 2.4, color: "#fff" })
|
|
2105
|
+
),
|
|
2106
|
+
m.file.status !== "sent" && m.id && /* @__PURE__ */ React.createElement(
|
|
2107
|
+
"button",
|
|
2108
|
+
{
|
|
2109
|
+
title: T.retry || "retry",
|
|
2110
|
+
disabled: btnBusy,
|
|
2111
|
+
onClick: (e) => {
|
|
2112
|
+
e.stopPropagation();
|
|
2113
|
+
if (!btnBusy)
|
|
2114
|
+
onRetry(m.id);
|
|
2115
|
+
},
|
|
2116
|
+
style: { background: "rgba(255,255,255,0.16)", border: "none", borderRadius: 6, cursor: btnBusy ? "wait" : "pointer", padding: 3, marginLeft: 2, display: "inline-flex", flexShrink: 0, opacity: btnBusy ? 0.5 : 1 }
|
|
2117
|
+
},
|
|
2118
|
+
/* @__PURE__ */ React.createElement(Icon, { name: "refresh", size: 13, stroke: 2.2, color: "#fff" })
|
|
2119
|
+
)
|
|
2120
|
+
);
|
|
2086
2121
|
}
|
|
2087
2122
|
return /* @__PURE__ */ React.createElement(
|
|
2088
2123
|
"div",
|
|
@@ -2874,6 +2909,39 @@ function DkPunkAvatar({ id, size, radius, fallbackSeed }) {
|
|
|
2874
2909
|
}
|
|
2875
2910
|
);
|
|
2876
2911
|
}
|
|
2912
|
+
const dkEnsAvCache = /* @__PURE__ */ new Map();
|
|
2913
|
+
function DkEnsAvatar({ userid, fallbackSeed, size, radius }) {
|
|
2914
|
+
const [pub, setPub] = React.useState(() => userid && dkEnsAvCache.has(userid) ? dkEnsAvCache.get(userid) : void 0);
|
|
2915
|
+
React.useEffect(() => {
|
|
2916
|
+
let dead = false;
|
|
2917
|
+
if (!userid) {
|
|
2918
|
+
setPub(null);
|
|
2919
|
+
return void 0;
|
|
2920
|
+
}
|
|
2921
|
+
if (dkEnsAvCache.has(userid)) {
|
|
2922
|
+
setPub(dkEnsAvCache.get(userid));
|
|
2923
|
+
return void 0;
|
|
2924
|
+
}
|
|
2925
|
+
fetch(`/api/ens-profile?userid=${encodeURIComponent(userid)}`).then((r) => r.json()).then((d) => {
|
|
2926
|
+
var _a;
|
|
2927
|
+
const v = d.ok && d.profile ? { avatarUrl: d.profile.avatarUrl || null, punkId: (_a = d.profile.punkId) != null ? _a : null } : null;
|
|
2928
|
+
dkEnsAvCache.set(userid, v);
|
|
2929
|
+
if (!dead)
|
|
2930
|
+
setPub(v);
|
|
2931
|
+
}).catch(() => {
|
|
2932
|
+
if (!dead)
|
|
2933
|
+
setPub(null);
|
|
2934
|
+
});
|
|
2935
|
+
return () => {
|
|
2936
|
+
dead = true;
|
|
2937
|
+
};
|
|
2938
|
+
}, [userid]);
|
|
2939
|
+
if (pub && pub.avatarUrl)
|
|
2940
|
+
return /* @__PURE__ */ React.createElement(DkImgAvatar, { url: pub.avatarUrl, seed: fallbackSeed || userid, size, radius });
|
|
2941
|
+
if (pub && pub.punkId != null)
|
|
2942
|
+
return /* @__PURE__ */ React.createElement(DkPunkAvatar, { id: pub.punkId, size, radius, fallbackSeed: fallbackSeed || userid });
|
|
2943
|
+
return /* @__PURE__ */ React.createElement(DkIdenticon, { seed: fallbackSeed || userid || "x", size, radius });
|
|
2944
|
+
}
|
|
2877
2945
|
function DkPunkPicker({ T, onPick, onClose }) {
|
|
2878
2946
|
const TYPES = ["any", "female", "male", "zombie", "ape", "alien"];
|
|
2879
2947
|
const tLabel = (t) => T && T["pt_" + t] || t;
|
|
@@ -3211,7 +3279,7 @@ function ProfileTab({ T, me, onEdit }) {
|
|
|
3211
3279
|
setEditing(false);
|
|
3212
3280
|
} }));
|
|
3213
3281
|
}
|
|
3214
|
-
Object.assign(window, { ProfileTab, DkPunkAvatar, DkPublicProfile });
|
|
3282
|
+
Object.assign(window, { ProfileTab, DkPunkAvatar, DkEnsAvatar, DkPublicProfile });
|
|
3215
3283
|
function DkDirAvatar({ p, size }) {
|
|
3216
3284
|
const [broken, setBroken] = React.useState(false);
|
|
3217
3285
|
if (p.punkId != null && !(p.avatar && !broken)) {
|
package/dist/server.js
CHANGED
|
@@ -956,8 +956,10 @@ export function startBeagleServer(opts) {
|
|
|
956
956
|
}
|
|
957
957
|
// ── File transfer ──────────────────────────────────────────────────
|
|
958
958
|
// Upload: the browser POSTs raw file bytes with ?userid=&name= in the
|
|
959
|
-
// query.
|
|
960
|
-
//
|
|
959
|
+
// query. The upload is saved INTO downloads/ (collision-renamed) and
|
|
960
|
+
// kept — the sender's own chip can then open / reveal / download the
|
|
961
|
+
// file just like the receiver's. (It used to stream to a temp file
|
|
962
|
+
// deleted right after send, which made every sent chip a dead end.)
|
|
961
963
|
if (req.method === "POST" && url === "/api/file-send") {
|
|
962
964
|
const q = new URL(req.url || "", "http://x").searchParams;
|
|
963
965
|
const userid = q.get("userid") || "";
|
|
@@ -970,17 +972,37 @@ export function startBeagleServer(opts) {
|
|
|
970
972
|
const os = await import("node:os");
|
|
971
973
|
const fs = await import("node:fs");
|
|
972
974
|
const fsp = await import("node:fs/promises");
|
|
973
|
-
|
|
974
|
-
|
|
975
|
+
// Keep the copy under downloads when available (normal case); fall
|
|
976
|
+
// back to the old ephemeral temp only when downloads are disabled.
|
|
977
|
+
const keep = !!opts.downloadsDir;
|
|
978
|
+
let destDir;
|
|
979
|
+
if (keep) {
|
|
980
|
+
destDir = opts.downloadsDir;
|
|
981
|
+
await fsp.mkdir(destDir, { recursive: true });
|
|
982
|
+
}
|
|
983
|
+
else {
|
|
984
|
+
destDir = await fsp.mkdtemp(join(os.tmpdir(), "agentnet-up-"));
|
|
985
|
+
}
|
|
986
|
+
let destName = safe;
|
|
987
|
+
let destPath = join(destDir, destName);
|
|
988
|
+
if (keep) {
|
|
989
|
+
const dot = safe.lastIndexOf(".");
|
|
990
|
+
const base = dot > 0 ? safe.slice(0, dot) : safe;
|
|
991
|
+
const ext = dot > 0 ? safe.slice(dot) : "";
|
|
992
|
+
for (let i = 1; existsSync(destPath); i++) {
|
|
993
|
+
destName = `${base}-${i}${ext}`;
|
|
994
|
+
destPath = join(destDir, destName);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
975
997
|
try {
|
|
976
998
|
await new Promise((resolve2, reject) => {
|
|
977
|
-
const ws = fs.createWriteStream(
|
|
999
|
+
const ws = fs.createWriteStream(destPath);
|
|
978
1000
|
req.pipe(ws);
|
|
979
1001
|
req.on("error", reject);
|
|
980
1002
|
ws.on("error", reject);
|
|
981
1003
|
ws.on("finish", () => resolve2());
|
|
982
1004
|
});
|
|
983
|
-
const r = await opts.call({ op: "file-send", userid, path:
|
|
1005
|
+
const r = await opts.call({ op: "file-send", userid, path: destPath });
|
|
984
1006
|
if (!r.ok) {
|
|
985
1007
|
sendJson(res, 502, r);
|
|
986
1008
|
return;
|
|
@@ -991,7 +1013,8 @@ export function startBeagleServer(opts) {
|
|
|
991
1013
|
sendJson(res, 500, { ok: false, error: e instanceof Error ? e.message : String(e) });
|
|
992
1014
|
}
|
|
993
1015
|
finally {
|
|
994
|
-
|
|
1016
|
+
if (!keep)
|
|
1017
|
+
await fsp.rm(destDir, { recursive: true, force: true }).catch(() => undefined);
|
|
995
1018
|
}
|
|
996
1019
|
return;
|
|
997
1020
|
}
|
package/package.json
CHANGED