@decentnetwork/beagle 0.1.113 → 0.1.114
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 +180 -19
- package/package.json +2 -2
package/dist/desktop/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
window.__DK_UI_VERSION="0.1.
|
|
1
|
+
window.__DK_UI_VERSION="0.1.114";
|
|
2
2
|
"use strict";
|
|
3
3
|
(() => {
|
|
4
4
|
// packages/beagle-ui/dist/beagle-ui.js
|
|
@@ -114,6 +114,8 @@ window.__DK_UI_VERSION="0.1.113";
|
|
|
114
114
|
plug: '<path d="M9 2v6M15 2v6"/><path d="M7 8h10v3a5 5 0 0 1-10 0V8Z"/><path d="M12 16v6"/>',
|
|
115
115
|
// A form with a filled-in field: the glyph for "Fill with Beagle" and any
|
|
116
116
|
// other host app that puts your identity into someone else's page.
|
|
117
|
+
// A pane pinned to the right edge — "keep this beside the page".
|
|
118
|
+
dock: '<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M14 4v16"/><path d="M17 9.5h1.5M17 13h1.5"/>',
|
|
117
119
|
form: '<rect x="4" y="3" width="16" height="18" rx="2"/><path d="M8 8h8M8 12h5"/><path d="M8 16h3"/><path d="m14.5 17.5 1.6 1.6 3.4-3.6"/>'
|
|
118
120
|
});
|
|
119
121
|
var H = {};
|
|
@@ -126,12 +128,16 @@ window.__DK_UI_VERSION="0.1.113";
|
|
|
126
128
|
var hostHideApps = null;
|
|
127
129
|
var hostApps = null;
|
|
128
130
|
var hostProfileSections = null;
|
|
131
|
+
var hostHeaderActions = null;
|
|
132
|
+
var hostIsProtocolMessage = null;
|
|
129
133
|
function setUiHost(host2) {
|
|
130
134
|
H = host2 || {};
|
|
131
135
|
hostTabs = Array.isArray(H.hostTabs) ? H.hostTabs : null;
|
|
132
136
|
hostHideApps = Array.isArray(H.hideApps) ? H.hideApps : null;
|
|
133
137
|
hostApps = Array.isArray(H.hostApps) ? H.hostApps : null;
|
|
134
138
|
hostProfileSections = Array.isArray(H.hostProfileSections) ? H.hostProfileSections : null;
|
|
139
|
+
hostHeaderActions = Array.isArray(H.hostHeaderActions) ? H.hostHeaderActions : null;
|
|
140
|
+
hostIsProtocolMessage = typeof H.isProtocolMessage === "function" ? H.isProtocolMessage : null;
|
|
135
141
|
hostNeedsWelcome = !!H.hostNeedsWelcome;
|
|
136
142
|
hostPrefersRtcFile = !!H.hostPrefersRtcFile;
|
|
137
143
|
hostHasKeyBackup = !!H.hostHasKeyBackup;
|
|
@@ -1487,6 +1493,98 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
1487
1493
|
}, [append, lang, onReloadFriends]);
|
|
1488
1494
|
return { thread, handle, removeFriends, adoptServicePeers };
|
|
1489
1495
|
}
|
|
1496
|
+
function protocolTest(rule) {
|
|
1497
|
+
if (typeof rule !== "function")
|
|
1498
|
+
return () => false;
|
|
1499
|
+
return (msg) => {
|
|
1500
|
+
if (!msg)
|
|
1501
|
+
return false;
|
|
1502
|
+
try {
|
|
1503
|
+
const v = rule(msg);
|
|
1504
|
+
return typeof v === "string" ? v || true : !!v;
|
|
1505
|
+
} catch (e) {
|
|
1506
|
+
return false;
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
function stripOwnPrefix(text) {
|
|
1511
|
+
return String(text == null ? "" : text).replace(/^you:\s*/i, "");
|
|
1512
|
+
}
|
|
1513
|
+
function lastMessageOf(peer) {
|
|
1514
|
+
const raw = String(peer && peer.lastMsg || "");
|
|
1515
|
+
return {
|
|
1516
|
+
text: stripOwnPrefix(raw),
|
|
1517
|
+
dir: peer && peer.lastDir || (/^you:\s*/i.test(raw) ? "out" : "in"),
|
|
1518
|
+
ts: peer && peer.lastTs || 0
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
function lastIsProtocol(peer, isProto) {
|
|
1522
|
+
const last = lastMessageOf(peer);
|
|
1523
|
+
if (!last.text)
|
|
1524
|
+
return false;
|
|
1525
|
+
return isProto(last);
|
|
1526
|
+
}
|
|
1527
|
+
function rosterRow(peer, isProto) {
|
|
1528
|
+
const hidden = lastIsProtocol(peer, isProto);
|
|
1529
|
+
return {
|
|
1530
|
+
preview: hidden ? "" : peer && peer.lastMsg || "",
|
|
1531
|
+
lastTime: hidden ? "" : peer && peer.lastTime || "",
|
|
1532
|
+
unread: hidden ? 0 : Number(peer && peer.unread || 0),
|
|
1533
|
+
activityTs: hidden ? 0 : Number(peer && peer.lastTs || 0),
|
|
1534
|
+
hidden
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
function rosterUnreadTotal(peers, isProto) {
|
|
1538
|
+
return (peers || []).reduce((n, p) => n + rosterRow(p, isProto).unread, 0);
|
|
1539
|
+
}
|
|
1540
|
+
function collapseProtocol(thread, isProto, lang) {
|
|
1541
|
+
const out = [];
|
|
1542
|
+
let run = null;
|
|
1543
|
+
const flush = () => {
|
|
1544
|
+
if (!run)
|
|
1545
|
+
return;
|
|
1546
|
+
out.push({
|
|
1547
|
+
protocol: true,
|
|
1548
|
+
id: `proto-${run.first.id || run.startIndex}`,
|
|
1549
|
+
count: run.items.length,
|
|
1550
|
+
label: run.label,
|
|
1551
|
+
dir: run.first.from === "me" ? "out" : "in",
|
|
1552
|
+
text: run.items.map((m) => m.text).join("\n"),
|
|
1553
|
+
line: protocolLine(run, lang)
|
|
1554
|
+
});
|
|
1555
|
+
run = null;
|
|
1556
|
+
};
|
|
1557
|
+
(thread || []).forEach((m, i) => {
|
|
1558
|
+
const hit = m && !m.day ? isProto({ text: m.text, dir: m && m.from === "me" ? "out" : "in", ts: m && m.ts }) : false;
|
|
1559
|
+
if (!hit || m && m.file) {
|
|
1560
|
+
flush();
|
|
1561
|
+
out.push(m);
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
if (!run)
|
|
1565
|
+
run = { first: m, startIndex: i, items: [], label: typeof hit === "string" ? hit : "" };
|
|
1566
|
+
if (!run.label && typeof hit === "string")
|
|
1567
|
+
run.label = hit;
|
|
1568
|
+
run.items.push(m);
|
|
1569
|
+
});
|
|
1570
|
+
flush();
|
|
1571
|
+
return out;
|
|
1572
|
+
}
|
|
1573
|
+
function protocolLine(run, lang) {
|
|
1574
|
+
const arrow = run.first.from === "me" ? "\u2192" : "\u2190";
|
|
1575
|
+
const n = run.items.length;
|
|
1576
|
+
if (run.label) {
|
|
1577
|
+
return `\u2699 ${arrow} ${run.label}${n > 1 ? ` \xB7 ${n}` : ""}`;
|
|
1578
|
+
}
|
|
1579
|
+
if (n > 1) {
|
|
1580
|
+
return lang === "zh" ? `\u2699 ${arrow} ${n} \u6761\u534F\u8BAE\u6D88\u606F` : `\u2699 ${arrow} ${n} protocol messages`;
|
|
1581
|
+
}
|
|
1582
|
+
return `\u2699 ${arrow} ${protocolSummary(run.first.text)}`;
|
|
1583
|
+
}
|
|
1584
|
+
function protocolSummary(text, max = 72) {
|
|
1585
|
+
const s = String(text == null ? "" : text).replace(/\s+/g, " ").trim();
|
|
1586
|
+
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
1587
|
+
}
|
|
1490
1588
|
var DK_CMD_HIST_KEY = "beagle.assistant.history";
|
|
1491
1589
|
var DK_CMD_HIST_MAX = 50;
|
|
1492
1590
|
function RequestsBlock({ T, requests, onAct }) {
|
|
@@ -1547,8 +1645,9 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
1547
1645
|
paddingLeft: 8
|
|
1548
1646
|
} }, r.descr), failed[r.id] && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11, color: "var(--danger)" } }, failed[r.id]), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 6 } }, /* @__PURE__ */ React.createElement(Btn, { tone: "ok", icon: "check", size: "sm", onClick: () => act(r, "accept"), style: { flex: 1 } }, T.accept), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", icon: "x", size: "sm", onClick: () => act(r, "reject"), style: { flex: 1 } }, T.reject))))));
|
|
1549
1647
|
}
|
|
1550
|
-
function PeerRow({ peer, T, active, onClick, muted }) {
|
|
1648
|
+
function PeerRow({ peer, T, active, onClick, muted, isProtocol }) {
|
|
1551
1649
|
const name = peer.alias || peer.userId;
|
|
1650
|
+
const row = rosterRow(peer, isProtocol || (() => false));
|
|
1552
1651
|
return /* @__PURE__ */ React.createElement("button", { onClick, style: {
|
|
1553
1652
|
display: "flex",
|
|
1554
1653
|
alignItems: "center",
|
|
@@ -1577,12 +1676,13 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
1577
1676
|
textOverflow: "ellipsis",
|
|
1578
1677
|
flex: 1,
|
|
1579
1678
|
minWidth: 0
|
|
1580
|
-
} }, dkRowPreview(
|
|
1679
|
+
} }, dkRowPreview(row.preview)))), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 4, flexShrink: 0 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, color: "var(--faint)" } }, row.lastTime), row.unread ? /* @__PURE__ */ React.createElement(Unread, { n: row.unread }) : peer.pending ? /* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 11, stroke: 2.2, color: "var(--warn, #d29922)", title: "friend request pending" }) : /* @__PURE__ */ React.createElement(StatusDot, { online: peer.online })));
|
|
1581
1680
|
}
|
|
1582
|
-
function dkActivityTs(p) {
|
|
1583
|
-
|
|
1681
|
+
function dkActivityTs(p, isProtocol) {
|
|
1682
|
+
const row = rosterRow(p, isProtocol || (() => false));
|
|
1683
|
+
return Math.max(row.activityTs, p.acceptedAt || 0, p.requestedAt || 0);
|
|
1584
1684
|
}
|
|
1585
|
-
function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, wide }) {
|
|
1685
|
+
function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, wide, isProtocol }) {
|
|
1586
1686
|
const [q, setQ] = React.useState("");
|
|
1587
1687
|
const [addr, setAddr] = React.useState("");
|
|
1588
1688
|
const [addState, setAddState] = React.useState(null);
|
|
@@ -1627,7 +1727,7 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
1627
1727
|
}).slice().sort((a, b) => {
|
|
1628
1728
|
if (!!a.pinned !== !!b.pinned)
|
|
1629
1729
|
return a.pinned ? -1 : 1;
|
|
1630
|
-
const ta = dkActivityTs(a), tb = dkActivityTs(b);
|
|
1730
|
+
const ta = dkActivityTs(a, isProtocol), tb = dkActivityTs(b, isProtocol);
|
|
1631
1731
|
if (ta !== tb)
|
|
1632
1732
|
return tb - ta;
|
|
1633
1733
|
return String(a.alias || a.userId || "").localeCompare(String(b.alias || b.userId || ""));
|
|
@@ -1666,7 +1766,7 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
1666
1766
|
fontSize: 11.5,
|
|
1667
1767
|
lineHeight: 1.35,
|
|
1668
1768
|
color: addState.kind === "err" ? "var(--bad, #e5484d)" : addState.kind === "ok" ? "var(--good, #46a758)" : "var(--faint)"
|
|
1669
|
-
} }, addState.msg), /* @__PURE__ */ React.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(Icon, { name: "search", size: 15, color: "var(--faint)", stroke: 2, style: { position: "absolute", left: 9 } }), /* @__PURE__ */ React.createElement("input", { value: q, onChange: (e) => setQ(e.target.value), placeholder: T.search, style: { ...inputStyle, paddingLeft: 30 } }))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", padding: "10px 8px 16px" } }, /* @__PURE__ */ React.createElement(RequestsBlock, { T, requests, onAct }), /* @__PURE__ */ React.createElement(Section, { label: T.peers, count: `${online}/${peers.length}`, style: { margin: "6px 4px 8px" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 2 } }, filtered.map((p) => /* @__PURE__ */ React.createElement(PeerRow, { key: p.id, peer: p, T, active: p.id === activeId, onClick: () => onSelect(p.id), muted: !!muted && muted.includes(p.id) })), !filtered.length && /* @__PURE__ */ React.createElement("div", { style: { padding: 16, textAlign: "center", fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)" } }, "no matches"))));
|
|
1769
|
+
} }, addState.msg), /* @__PURE__ */ React.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(Icon, { name: "search", size: 15, color: "var(--faint)", stroke: 2, style: { position: "absolute", left: 9 } }), /* @__PURE__ */ React.createElement("input", { value: q, onChange: (e) => setQ(e.target.value), placeholder: T.search, style: { ...inputStyle, paddingLeft: 30 } }))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", padding: "10px 8px 16px" } }, /* @__PURE__ */ React.createElement(RequestsBlock, { T, requests, onAct }), /* @__PURE__ */ React.createElement(Section, { label: T.peers, count: `${online}/${peers.length}`, style: { margin: "6px 4px 8px" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 2 } }, filtered.map((p) => /* @__PURE__ */ React.createElement(PeerRow, { key: p.id, peer: p, T, active: p.id === activeId, onClick: () => onSelect(p.id), muted: !!muted && muted.includes(p.id), isProtocol })), !filtered.length && /* @__PURE__ */ React.createElement("div", { style: { padding: 16, textAlign: "center", fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)" } }, "no matches"))));
|
|
1670
1770
|
}
|
|
1671
1771
|
var inputStyle = {
|
|
1672
1772
|
flex: 1,
|
|
@@ -2666,7 +2766,7 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
2666
2766
|
) : mine && m.via === "offline" && m.status !== "queued" && m.status !== "sending" ? /* @__PURE__ */ React.createElement(Icon, { name: "check", size: 12, stroke: 2.2, color: "#f59e0b", title: T.viaOutOffline || "posted to the offline relay \u2014 they get it when they come online" }) : mine && m.status === "sent" && m.confirmed === false ? /* @__PURE__ */ React.createElement(Icon, { name: "check", size: 12, stroke: 2.2, color: "var(--faint)", title: T.sentUnconfirmed || "sent \u2014 not confirmed by the peer" }) : mine && m.status && /* @__PURE__ */ React.createElement(Icon, { name: "checkCheck", size: 12, stroke: 2.2, color: m.status === "read" ? "var(--accent)" : "var(--faint)" })))
|
|
2667
2767
|
);
|
|
2668
2768
|
}
|
|
2669
|
-
function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, muted, onToggleMute, phone, onBack }) {
|
|
2769
|
+
function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, isProtocol, muted, onToggleMute, phone, onBack }) {
|
|
2670
2770
|
const scrollRef = React.useRef(null);
|
|
2671
2771
|
const prependRef = React.useRef(null);
|
|
2672
2772
|
const [earlierBusy, setEarlierBusy] = React.useState(false);
|
|
@@ -3088,7 +3188,15 @@ ${peer.address}`
|
|
|
3088
3188
|
fontSize: 12.5,
|
|
3089
3189
|
color: "var(--text)"
|
|
3090
3190
|
} }, /* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 13, stroke: 2.2, color: "#d29922" }), /* @__PURE__ */ React.createElement("span", null, T.pendingFriend || "Waiting for them to accept your friend request \u2014 messages will queue and deliver once they do.")),
|
|
3091
|
-
/* @__PURE__ */ React.createElement("div", { ref: scrollRef, onScroll: updateFollowScroll, style: { flex: 1, overflow: "auto", padding: phone ? "12px 10px" : "18px 22px", WebkitOverflowScrolling: "touch" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 1200, margin: "0 auto" } }, hasEarlier && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "center", margin: "4px 0 10px" } }, /* @__PURE__ */ React.createElement(Btn, { size: "sm", onClick: loadEarlier, disabled: earlierBusy }, earlierBusy ? T.loadingEarlier || "Loading\u2026" : T.loadEarlier || "Load earlier messages")), thread.filter((m) => m.day || !hidden.has(m.id)).map((m, i) => m.day ? /* @__PURE__ */ React.createElement("div", { key: i, style: { display: "flex", justifyContent: "center", margin: "14px 0" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, fontWeight: 600, color: "var(--faint)", background: "var(--chip)", padding: "3px 10px", borderRadius: 999 } }, m.day)) : /* @__PURE__ */ React.createElement(
|
|
3191
|
+
/* @__PURE__ */ React.createElement("div", { ref: scrollRef, onScroll: updateFollowScroll, style: { flex: 1, overflow: "auto", padding: phone ? "12px 10px" : "18px 22px", WebkitOverflowScrolling: "touch" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 1200, margin: "0 auto" } }, hasEarlier && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "center", margin: "4px 0 10px" } }, /* @__PURE__ */ React.createElement(Btn, { size: "sm", onClick: loadEarlier, disabled: earlierBusy }, earlierBusy ? T.loadingEarlier || "Loading\u2026" : T.loadEarlier || "Load earlier messages")), collapseProtocol(thread, isProtocol || (() => false), lang).filter((m) => m.day || m.protocol || !hidden.has(m.id)).map((m, i) => m.day ? /* @__PURE__ */ React.createElement("div", { key: i, style: { display: "flex", justifyContent: "center", margin: "14px 0" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, fontWeight: 600, color: "var(--faint)", background: "var(--chip)", padding: "3px 10px", borderRadius: 999 } }, m.day)) : m.protocol ? /* @__PURE__ */ React.createElement("div", { key: m.id || i, style: { display: "flex", justifyContent: "center", margin: "4px 0" } }, /* @__PURE__ */ React.createElement("span", { title: m.text, style: {
|
|
3192
|
+
fontFamily: "var(--mono)",
|
|
3193
|
+
fontSize: 10.5,
|
|
3194
|
+
color: "var(--faint)",
|
|
3195
|
+
maxWidth: "86%",
|
|
3196
|
+
whiteSpace: "nowrap",
|
|
3197
|
+
overflow: "hidden",
|
|
3198
|
+
textOverflow: "ellipsis"
|
|
3199
|
+
} }, m.line)) : /* @__PURE__ */ React.createElement(
|
|
3092
3200
|
Msg,
|
|
3093
3201
|
{
|
|
3094
3202
|
key: m.id || i,
|
|
@@ -3259,12 +3367,12 @@ ${peer.address}`
|
|
|
3259
3367
|
function ChatEmpty({ T }) {
|
|
3260
3368
|
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));
|
|
3261
3369
|
}
|
|
3262
|
-
function ChatTab({ T, lang, peers, requests, activeId, thread, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, prefillAddr, onPrefillConsumed, muted, onToggleMute, phone, onBack }) {
|
|
3370
|
+
function ChatTab({ T, lang, peers, requests, activeId, thread, isProtocol, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, prefillAddr, onPrefillConsumed, muted, onToggleMute, phone, onBack }) {
|
|
3263
3371
|
const peer = peers.find((p) => p.id === activeId);
|
|
3264
3372
|
if (phone) {
|
|
3265
|
-
return peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, muted: !!muted && muted.includes(peer.id), onToggleMute, phone: true, onBack }) : /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, wide: true });
|
|
3373
|
+
return peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, isProtocol, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, muted: !!muted && muted.includes(peer.id), onToggleMute, phone: true, onBack }) : /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, isProtocol, wide: true });
|
|
3266
3374
|
}
|
|
3267
|
-
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, prefillAddr, onPrefillConsumed, muted }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, muted: !!muted && muted.includes(peer.id), onToggleMute }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
|
|
3375
|
+
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, prefillAddr, onPrefillConsumed, muted, isProtocol }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, isProtocol, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, muted: !!muted && muted.includes(peer.id), onToggleMute }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
|
|
3268
3376
|
}
|
|
3269
3377
|
Object.assign(window, { ChatTab });
|
|
3270
3378
|
function CopyBtn({ value, copiedText = "Copied", copyFailedText = "Copy failed", copyTitle = "Copy" }) {
|
|
@@ -5888,7 +5996,7 @@ ${peer.address}`
|
|
|
5888
5996
|
function dkLooking() {
|
|
5889
5997
|
return document.visibilityState === "visible" && document.hasFocus();
|
|
5890
5998
|
}
|
|
5891
|
-
function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true, flash = true, muted }) {
|
|
5999
|
+
function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true, flash = true, muted, isProtocol }) {
|
|
5892
6000
|
const prev = React.useRef(null);
|
|
5893
6001
|
const mutedRef = React.useRef(/* @__PURE__ */ new Set());
|
|
5894
6002
|
mutedRef.current = new Set(muted || []);
|
|
@@ -5918,7 +6026,8 @@ ${peer.address}`
|
|
|
5918
6026
|
const byId = /* @__PURE__ */ new Map();
|
|
5919
6027
|
for (const p of peers || []) {
|
|
5920
6028
|
const dir = p.lastDir || (String(p.lastMsg || "").startsWith("you: ") ? "out" : "in");
|
|
5921
|
-
|
|
6029
|
+
const quiet = typeof isProtocol === "function" && lastIsProtocol(p, isProtocol);
|
|
6030
|
+
byId.set(p.id, { ts: p.lastTs || 0, dir, quiet });
|
|
5922
6031
|
}
|
|
5923
6032
|
const reqIds = new Set((requests || []).map((r) => r.id));
|
|
5924
6033
|
const was = prev.current;
|
|
@@ -5929,6 +6038,8 @@ ${peer.address}`
|
|
|
5929
6038
|
for (const [id, cur] of byId) {
|
|
5930
6039
|
if (cur.dir !== "in" || !cur.ts)
|
|
5931
6040
|
continue;
|
|
6041
|
+
if (cur.quiet)
|
|
6042
|
+
continue;
|
|
5932
6043
|
const old = was.byId.get(id);
|
|
5933
6044
|
if (old ? cur.ts <= old.ts : Date.now() - cur.ts > 3e4)
|
|
5934
6045
|
continue;
|
|
@@ -6772,6 +6883,7 @@ ${peer.address}`
|
|
|
6772
6883
|
const rowPad = t.density === "comfortable" ? "11px 12px" : "7px 10px";
|
|
6773
6884
|
const mutedIds = Array.isArray(t.alertMuted) ? t.alertMuted : [];
|
|
6774
6885
|
const onToggleMute = (id) => setTweak("alertMuted", mutedIds.includes(id) ? mutedIds.filter((x) => x !== id) : mutedIds.concat(id));
|
|
6886
|
+
const isProtocol = React.useMemo(() => protocolTest(hostIsProtocolMessage), []);
|
|
6775
6887
|
useIncomingAlerts({
|
|
6776
6888
|
T,
|
|
6777
6889
|
peers,
|
|
@@ -6780,7 +6892,8 @@ ${peer.address}`
|
|
|
6780
6892
|
activeId,
|
|
6781
6893
|
sound: t.alertSound !== false,
|
|
6782
6894
|
flash: t.alertFlash !== false,
|
|
6783
|
-
muted: mutedIds
|
|
6895
|
+
muted: mutedIds,
|
|
6896
|
+
isProtocol
|
|
6784
6897
|
});
|
|
6785
6898
|
const assistant = useAssistant({
|
|
6786
6899
|
lang: t.lang,
|
|
@@ -6814,6 +6927,54 @@ ${peer.address}`
|
|
|
6814
6927
|
});
|
|
6815
6928
|
const phone = useIsPhone();
|
|
6816
6929
|
const [sharing, setSharing] = React.useState(false);
|
|
6930
|
+
const headerActions = (hostHeaderActions || []).filter((a) => a && a.id && typeof a.onClick === "function");
|
|
6931
|
+
const headerActionBtns = headerActions.map((a) => {
|
|
6932
|
+
let on = false;
|
|
6933
|
+
try {
|
|
6934
|
+
on = typeof a.active === "function" ? !!a.active() : false;
|
|
6935
|
+
} catch (e) {
|
|
6936
|
+
on = false;
|
|
6937
|
+
}
|
|
6938
|
+
const label = pickText(a.label, t.lang, a.id);
|
|
6939
|
+
return /* @__PURE__ */ React.createElement(
|
|
6940
|
+
"button",
|
|
6941
|
+
{
|
|
6942
|
+
key: a.id,
|
|
6943
|
+
title: label,
|
|
6944
|
+
"aria-label": label,
|
|
6945
|
+
"aria-pressed": on,
|
|
6946
|
+
onClick: () => {
|
|
6947
|
+
try {
|
|
6948
|
+
a.onClick({ T, t, me, activeId, phone });
|
|
6949
|
+
} catch (e) {
|
|
6950
|
+
}
|
|
6951
|
+
},
|
|
6952
|
+
style: {
|
|
6953
|
+
width: 32,
|
|
6954
|
+
height: 32,
|
|
6955
|
+
flexShrink: 0,
|
|
6956
|
+
borderRadius: 8,
|
|
6957
|
+
cursor: "pointer",
|
|
6958
|
+
padding: 0,
|
|
6959
|
+
border: "1px solid " + (on ? "var(--accent)" : "var(--line)"),
|
|
6960
|
+
background: on ? "color-mix(in oklab, var(--accent), transparent 86%)" : "transparent",
|
|
6961
|
+
display: "flex",
|
|
6962
|
+
alignItems: "center",
|
|
6963
|
+
justifyContent: "center"
|
|
6964
|
+
}
|
|
6965
|
+
},
|
|
6966
|
+
/* @__PURE__ */ React.createElement(
|
|
6967
|
+
Icon,
|
|
6968
|
+
{
|
|
6969
|
+
name: typeof a.icon === "string" ? a.icon : void 0,
|
|
6970
|
+
svgPath: a.icon && typeof a.icon === "object" ? a.icon.svgPath : void 0,
|
|
6971
|
+
size: 16,
|
|
6972
|
+
stroke: 2,
|
|
6973
|
+
color: on ? "var(--accent)" : "var(--dim)"
|
|
6974
|
+
}
|
|
6975
|
+
)
|
|
6976
|
+
);
|
|
6977
|
+
});
|
|
6817
6978
|
const shareBtn = /* @__PURE__ */ React.createElement(
|
|
6818
6979
|
"button",
|
|
6819
6980
|
{
|
|
@@ -6984,7 +7145,7 @@ ${peer.address}`
|
|
|
6984
7145
|
const lastPeople = React.useRef("here");
|
|
6985
7146
|
if (peopleTab)
|
|
6986
7147
|
lastPeople.current = tab;
|
|
6987
|
-
const unreadTotal = (peers
|
|
7148
|
+
const unreadTotal = rosterUnreadTotal(peers, isProtocol) + (requests || []).length;
|
|
6988
7149
|
const NAV_FOR = {
|
|
6989
7150
|
chat: { icon: "message", label: T.chat, active: tab === "chat", badge: unreadTotal, go: () => setTab("chat") },
|
|
6990
7151
|
people: { icon: "users", label: T.people, active: peopleTab, go: () => setTab(lastPeople.current) },
|
|
@@ -7028,7 +7189,7 @@ ${peer.address}`
|
|
|
7028
7189
|
display: "flex",
|
|
7029
7190
|
border: "1px solid " + (tab === "profile" ? "var(--line)" : "transparent"),
|
|
7030
7191
|
background: tab === "profile" ? "var(--panel-2)" : "transparent"
|
|
7031
|
-
} }, /* @__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" } }, !inConversation && /* @__PURE__ */ React.createElement(DkBrowserNotice, { lang: t.lang }), !inConversation && /* @__PURE__ */ React.createElement("div", { style: { height: 46, boxSizing: "content-box", paddingTop: phone ? "env(safe-area-inset-top, 0px)" : 0, flexShrink: 0, borderBottom: "1px solid var(--line)", background: "var(--panel)", display: "flex", alignItems: "center", gap: phone ? 8 : 12, paddingLeft: phone ? 12 : 16, paddingRight: phone ? 12 : 16 } }, /* @__PURE__ */ React.createElement(Brand, { T, style: { gap: 12, flexShrink: 0 } }, /* @__PURE__ */ React.createElement("svg", { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: "var(--accent)", strokeWidth: 2.2, strokeLinecap: "round", strokeLinejoin: "round", style: { display: "block", flexShrink: 0 } }, /* @__PURE__ */ React.createElement("path", { d: "m4.5 17 6-6-6-6" }), /* @__PURE__ */ React.createElement("path", { d: "M12 18.5h7.5" })), /* @__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)", whiteSpace: "nowrap" } }, "\xB7 ", (nav.find((n) => n.id === tab) || { label: T.profile }).label.toLowerCase()), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), phone ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(StatusDot, { online: me.online }), shareBtn, /* @__PURE__ */ React.createElement("button", { onClick: () => setTab("profile"), title: T.profile, style: { padding: 0, border: "none", background: "transparent", cursor: "pointer", display: "flex" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 28, radius: 7, dot: false }))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__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)" } }), shareBtn, /* @__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)))), phone && peopleTab && /* @__PURE__ */ React.createElement("div", { style: { flexShrink: 0, display: "flex", gap: 6, padding: "8px 12px", borderBottom: "1px solid var(--line)", background: "var(--panel)", overflowX: "auto" } }, nav.filter((n) => DK_PEOPLE_TABS.includes(n.id)).map((n) => /* @__PURE__ */ React.createElement("button", { key: n.id, onClick: () => setTab(n.id), style: {
|
|
7192
|
+
} }, /* @__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" } }, !inConversation && /* @__PURE__ */ React.createElement(DkBrowserNotice, { lang: t.lang }), !inConversation && /* @__PURE__ */ React.createElement("div", { style: { height: 46, boxSizing: "content-box", paddingTop: phone ? "env(safe-area-inset-top, 0px)" : 0, flexShrink: 0, borderBottom: "1px solid var(--line)", background: "var(--panel)", display: "flex", alignItems: "center", gap: phone ? 8 : 12, paddingLeft: phone ? 12 : 16, paddingRight: phone ? 12 : 16 } }, /* @__PURE__ */ React.createElement(Brand, { T, style: { gap: 12, flexShrink: 0 } }, /* @__PURE__ */ React.createElement("svg", { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: "var(--accent)", strokeWidth: 2.2, strokeLinecap: "round", strokeLinejoin: "round", style: { display: "block", flexShrink: 0 } }, /* @__PURE__ */ React.createElement("path", { d: "m4.5 17 6-6-6-6" }), /* @__PURE__ */ React.createElement("path", { d: "M12 18.5h7.5" })), /* @__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)", whiteSpace: "nowrap" } }, "\xB7 ", (nav.find((n) => n.id === tab) || { label: T.profile }).label.toLowerCase()), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), phone ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(StatusDot, { online: me.online }), headerActionBtns, shareBtn, /* @__PURE__ */ React.createElement("button", { onClick: () => setTab("profile"), title: T.profile, style: { padding: 0, border: "none", background: "transparent", cursor: "pointer", display: "flex" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 28, radius: 7, dot: false }))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__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)" } }), headerActionBtns, shareBtn, /* @__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)))), phone && peopleTab && /* @__PURE__ */ React.createElement("div", { style: { flexShrink: 0, display: "flex", gap: 6, padding: "8px 12px", borderBottom: "1px solid var(--line)", background: "var(--panel)", overflowX: "auto" } }, nav.filter((n) => DK_PEOPLE_TABS.includes(n.id)).map((n) => /* @__PURE__ */ React.createElement("button", { key: n.id, onClick: () => setTab(n.id), style: {
|
|
7032
7193
|
display: "inline-flex",
|
|
7033
7194
|
alignItems: "center",
|
|
7034
7195
|
gap: 6,
|
|
@@ -7042,7 +7203,7 @@ ${peer.address}`
|
|
|
7042
7203
|
fontWeight: 600,
|
|
7043
7204
|
background: tab === n.id ? "var(--accent)" : "transparent",
|
|
7044
7205
|
color: tab === n.id ? "#fff" : "var(--dim)"
|
|
7045
|
-
} }, /* @__PURE__ */ React.createElement(Icon, { name: n.icon, size: 14, stroke: 2, color: tab === n.id ? "#fff" : "var(--dim)" }), n.label))), tab === "chat" && /* @__PURE__ */ React.createElement(ChatTab, { T, lang: t.lang, phone, onBack: closeConversation, peers, requests, activeId, thread: activeId === ASSISTANT_ID ? assistant.thread : data.threads[activeId], onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread: () => activeId && data.loadThread(activeId), hasEarlier: !!(activeId && activeId !== ASSISTANT_ID && data.earlier[activeId]), onAssistantRemove: activeId === ASSISTANT_ID ? assistant.removeFriends : null, onAssistantAdopt: activeId === ASSISTANT_ID ? assistant.adoptServicePeers : null, onLoadEarlier: () => activeId && data.loadEarlier(activeId), prefillAddr: pendingAddr, onPrefillConsumed: () => setPendingAddr(""), muted: mutedIds, onToggleMute }), tab === "here" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "bridge", peers, meId: me.userId, onAdd, onOpenChat }), tab === "recommended" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "recommended", peers, meId: me.userId, onAdd, onOpenChat }), tab === "registered" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "registered", peers, meId: me.userId, onAdd, onOpenChat }), tab === "network" && /* @__PURE__ */ React.createElement(NetworkTab, { T, me, peers, exits, activeExit, reqCount: requests.length, onSetExit, onOpenChat, backend, onArmLan: armLan, onCancelLan: cancelLan }), tab === "apps" && /* @__PURE__ */ React.createElement(AppsTab, { T, t, me, peers, phone, setTab }), tabs.map((x) => x.kind === "host" && tab === x.id ? /* @__PURE__ */ React.createElement(HostTabPane, { key: x.id, tab: x, props: { T, t, me, peers, phone, setTab }, lang: t.lang }) : null), tab === "profile" && /* @__PURE__ */ React.createElement(
|
|
7206
|
+
} }, /* @__PURE__ */ React.createElement(Icon, { name: n.icon, size: 14, stroke: 2, color: tab === n.id ? "#fff" : "var(--dim)" }), n.label))), tab === "chat" && /* @__PURE__ */ React.createElement(ChatTab, { T, lang: t.lang, phone, isProtocol, onBack: closeConversation, peers, requests, activeId, thread: activeId === ASSISTANT_ID ? assistant.thread : data.threads[activeId], onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread: () => activeId && data.loadThread(activeId), hasEarlier: !!(activeId && activeId !== ASSISTANT_ID && data.earlier[activeId]), onAssistantRemove: activeId === ASSISTANT_ID ? assistant.removeFriends : null, onAssistantAdopt: activeId === ASSISTANT_ID ? assistant.adoptServicePeers : null, onLoadEarlier: () => activeId && data.loadEarlier(activeId), prefillAddr: pendingAddr, onPrefillConsumed: () => setPendingAddr(""), muted: mutedIds, onToggleMute }), tab === "here" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "bridge", peers, meId: me.userId, onAdd, onOpenChat }), tab === "recommended" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "recommended", peers, meId: me.userId, onAdd, onOpenChat }), tab === "registered" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "registered", peers, meId: me.userId, onAdd, onOpenChat }), tab === "network" && /* @__PURE__ */ React.createElement(NetworkTab, { T, me, peers, exits, activeExit, reqCount: requests.length, onSetExit, onOpenChat, backend, onArmLan: armLan, onCancelLan: cancelLan }), tab === "apps" && /* @__PURE__ */ React.createElement(AppsTab, { T, t, me, peers, phone, setTab }), tabs.map((x) => x.kind === "host" && tab === x.id ? /* @__PURE__ */ React.createElement(HostTabPane, { key: x.id, tab: x, props: { T, t, me, peers, phone, setTab }, lang: t.lang }) : null), tab === "profile" && /* @__PURE__ */ React.createElement(
|
|
7046
7207
|
ProfileTab,
|
|
7047
7208
|
{
|
|
7048
7209
|
T,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/beagle",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.114",
|
|
4
4
|
"description": "Beagle — P2P chat, file transfer and calls for regular users, on the Decent Network. No admin privilege required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"yargs": "^17.7.2"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@decentnetwork/beagle-ui": "^0.2.
|
|
38
|
+
"@decentnetwork/beagle-ui": "^0.2.28",
|
|
39
39
|
"@types/js-yaml": "^4.0.9",
|
|
40
40
|
"@types/node": "^20.11.0",
|
|
41
41
|
"@types/yargs": "^17.0.32",
|