@decentnetwork/beagle-ui 0.2.27 → 0.2.28
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/beagle-ui.js +182 -19
- package/package.json +1 -1
package/dist/beagle-ui.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const BEAGLE_UI_VERSION = "0.2.
|
|
1
|
+
export const BEAGLE_UI_VERSION = "0.2.28";
|
|
2
2
|
// src/icons.jsx
|
|
3
3
|
var ICON_PATHS = {
|
|
4
4
|
// ---- tab bar (the four must feel like one set) ----
|
|
@@ -114,6 +114,8 @@ Object.assign(ICON_PATHS, {
|
|
|
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
|
|
|
@@ -128,12 +130,16 @@ var hostTabs = null;
|
|
|
128
130
|
var hostHideApps = null;
|
|
129
131
|
var hostApps = null;
|
|
130
132
|
var hostProfileSections = null;
|
|
133
|
+
var hostHeaderActions = null;
|
|
134
|
+
var hostIsProtocolMessage = null;
|
|
131
135
|
function setUiHost(host) {
|
|
132
136
|
H = host || {};
|
|
133
137
|
hostTabs = Array.isArray(H.hostTabs) ? H.hostTabs : null;
|
|
134
138
|
hostHideApps = Array.isArray(H.hideApps) ? H.hideApps : null;
|
|
135
139
|
hostApps = Array.isArray(H.hostApps) ? H.hostApps : null;
|
|
136
140
|
hostProfileSections = Array.isArray(H.hostProfileSections) ? H.hostProfileSections : null;
|
|
141
|
+
hostHeaderActions = Array.isArray(H.hostHeaderActions) ? H.hostHeaderActions : null;
|
|
142
|
+
hostIsProtocolMessage = typeof H.isProtocolMessage === "function" ? H.isProtocolMessage : null;
|
|
137
143
|
hostNeedsWelcome = !!H.hostNeedsWelcome;
|
|
138
144
|
hostPrefersRtcFile = !!H.hostPrefersRtcFile;
|
|
139
145
|
hostHasKeyBackup = !!H.hostHasKeyBackup;
|
|
@@ -1500,6 +1506,100 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
1500
1506
|
return { thread, handle, removeFriends, adoptServicePeers };
|
|
1501
1507
|
}
|
|
1502
1508
|
|
|
1509
|
+
// src/host-messages.js
|
|
1510
|
+
function protocolTest(rule) {
|
|
1511
|
+
if (typeof rule !== "function")
|
|
1512
|
+
return () => false;
|
|
1513
|
+
return (msg) => {
|
|
1514
|
+
if (!msg)
|
|
1515
|
+
return false;
|
|
1516
|
+
try {
|
|
1517
|
+
const v = rule(msg);
|
|
1518
|
+
return typeof v === "string" ? v || true : !!v;
|
|
1519
|
+
} catch (e) {
|
|
1520
|
+
return false;
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
function stripOwnPrefix(text) {
|
|
1525
|
+
return String(text == null ? "" : text).replace(/^you:\s*/i, "");
|
|
1526
|
+
}
|
|
1527
|
+
function lastMessageOf(peer) {
|
|
1528
|
+
const raw = String(peer && peer.lastMsg || "");
|
|
1529
|
+
return {
|
|
1530
|
+
text: stripOwnPrefix(raw),
|
|
1531
|
+
dir: peer && peer.lastDir || (/^you:\s*/i.test(raw) ? "out" : "in"),
|
|
1532
|
+
ts: peer && peer.lastTs || 0
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
function lastIsProtocol(peer, isProto) {
|
|
1536
|
+
const last = lastMessageOf(peer);
|
|
1537
|
+
if (!last.text)
|
|
1538
|
+
return false;
|
|
1539
|
+
return isProto(last);
|
|
1540
|
+
}
|
|
1541
|
+
function rosterRow(peer, isProto) {
|
|
1542
|
+
const hidden = lastIsProtocol(peer, isProto);
|
|
1543
|
+
return {
|
|
1544
|
+
preview: hidden ? "" : peer && peer.lastMsg || "",
|
|
1545
|
+
lastTime: hidden ? "" : peer && peer.lastTime || "",
|
|
1546
|
+
unread: hidden ? 0 : Number(peer && peer.unread || 0),
|
|
1547
|
+
activityTs: hidden ? 0 : Number(peer && peer.lastTs || 0),
|
|
1548
|
+
hidden
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
function rosterUnreadTotal(peers, isProto) {
|
|
1552
|
+
return (peers || []).reduce((n, p) => n + rosterRow(p, isProto).unread, 0);
|
|
1553
|
+
}
|
|
1554
|
+
function collapseProtocol(thread, isProto, lang) {
|
|
1555
|
+
const out = [];
|
|
1556
|
+
let run = null;
|
|
1557
|
+
const flush = () => {
|
|
1558
|
+
if (!run)
|
|
1559
|
+
return;
|
|
1560
|
+
out.push({
|
|
1561
|
+
protocol: true,
|
|
1562
|
+
id: `proto-${run.first.id || run.startIndex}`,
|
|
1563
|
+
count: run.items.length,
|
|
1564
|
+
label: run.label,
|
|
1565
|
+
dir: run.first.from === "me" ? "out" : "in",
|
|
1566
|
+
text: run.items.map((m) => m.text).join("\n"),
|
|
1567
|
+
line: protocolLine(run, lang)
|
|
1568
|
+
});
|
|
1569
|
+
run = null;
|
|
1570
|
+
};
|
|
1571
|
+
(thread || []).forEach((m, i) => {
|
|
1572
|
+
const hit = m && !m.day ? isProto({ text: m.text, dir: m && m.from === "me" ? "out" : "in", ts: m && m.ts }) : false;
|
|
1573
|
+
if (!hit || m && m.file) {
|
|
1574
|
+
flush();
|
|
1575
|
+
out.push(m);
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
if (!run)
|
|
1579
|
+
run = { first: m, startIndex: i, items: [], label: typeof hit === "string" ? hit : "" };
|
|
1580
|
+
if (!run.label && typeof hit === "string")
|
|
1581
|
+
run.label = hit;
|
|
1582
|
+
run.items.push(m);
|
|
1583
|
+
});
|
|
1584
|
+
flush();
|
|
1585
|
+
return out;
|
|
1586
|
+
}
|
|
1587
|
+
function protocolLine(run, lang) {
|
|
1588
|
+
const arrow = run.first.from === "me" ? "\u2192" : "\u2190";
|
|
1589
|
+
const n = run.items.length;
|
|
1590
|
+
if (run.label) {
|
|
1591
|
+
return `\u2699 ${arrow} ${run.label}${n > 1 ? ` \xB7 ${n}` : ""}`;
|
|
1592
|
+
}
|
|
1593
|
+
if (n > 1) {
|
|
1594
|
+
return lang === "zh" ? `\u2699 ${arrow} ${n} \u6761\u534F\u8BAE\u6D88\u606F` : `\u2699 ${arrow} ${n} protocol messages`;
|
|
1595
|
+
}
|
|
1596
|
+
return `\u2699 ${arrow} ${protocolSummary(run.first.text)}`;
|
|
1597
|
+
}
|
|
1598
|
+
function protocolSummary(text, max = 72) {
|
|
1599
|
+
const s = String(text == null ? "" : text).replace(/\s+/g, " ").trim();
|
|
1600
|
+
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1503
1603
|
// src/chat.jsx
|
|
1504
1604
|
var DK_CMD_HIST_KEY = "beagle.assistant.history";
|
|
1505
1605
|
var DK_CMD_HIST_MAX = 50;
|
|
@@ -1561,8 +1661,9 @@ function RequestsBlock({ T, requests, onAct }) {
|
|
|
1561
1661
|
paddingLeft: 8
|
|
1562
1662
|
} }, 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))))));
|
|
1563
1663
|
}
|
|
1564
|
-
function PeerRow({ peer, T, active, onClick, muted }) {
|
|
1664
|
+
function PeerRow({ peer, T, active, onClick, muted, isProtocol }) {
|
|
1565
1665
|
const name = peer.alias || peer.userId;
|
|
1666
|
+
const row = rosterRow(peer, isProtocol || (() => false));
|
|
1566
1667
|
return /* @__PURE__ */ React.createElement("button", { onClick, style: {
|
|
1567
1668
|
display: "flex",
|
|
1568
1669
|
alignItems: "center",
|
|
@@ -1591,12 +1692,13 @@ function PeerRow({ peer, T, active, onClick, muted }) {
|
|
|
1591
1692
|
textOverflow: "ellipsis",
|
|
1592
1693
|
flex: 1,
|
|
1593
1694
|
minWidth: 0
|
|
1594
|
-
} }, dkRowPreview(
|
|
1695
|
+
} }, 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 })));
|
|
1595
1696
|
}
|
|
1596
|
-
function dkActivityTs(p) {
|
|
1597
|
-
|
|
1697
|
+
function dkActivityTs(p, isProtocol) {
|
|
1698
|
+
const row = rosterRow(p, isProtocol || (() => false));
|
|
1699
|
+
return Math.max(row.activityTs, p.acceptedAt || 0, p.requestedAt || 0);
|
|
1598
1700
|
}
|
|
1599
|
-
function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, wide }) {
|
|
1701
|
+
function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, wide, isProtocol }) {
|
|
1600
1702
|
const [q, setQ] = React.useState("");
|
|
1601
1703
|
const [addr, setAddr] = React.useState("");
|
|
1602
1704
|
const [addState, setAddState] = React.useState(null);
|
|
@@ -1641,7 +1743,7 @@ function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, pre
|
|
|
1641
1743
|
}).slice().sort((a, b) => {
|
|
1642
1744
|
if (!!a.pinned !== !!b.pinned)
|
|
1643
1745
|
return a.pinned ? -1 : 1;
|
|
1644
|
-
const ta = dkActivityTs(a), tb = dkActivityTs(b);
|
|
1746
|
+
const ta = dkActivityTs(a, isProtocol), tb = dkActivityTs(b, isProtocol);
|
|
1645
1747
|
if (ta !== tb)
|
|
1646
1748
|
return tb - ta;
|
|
1647
1749
|
return String(a.alias || a.userId || "").localeCompare(String(b.alias || b.userId || ""));
|
|
@@ -1680,7 +1782,7 @@ function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, pre
|
|
|
1680
1782
|
fontSize: 11.5,
|
|
1681
1783
|
lineHeight: 1.35,
|
|
1682
1784
|
color: addState.kind === "err" ? "var(--bad, #e5484d)" : addState.kind === "ok" ? "var(--good, #46a758)" : "var(--faint)"
|
|
1683
|
-
} }, 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"))));
|
|
1785
|
+
} }, 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"))));
|
|
1684
1786
|
}
|
|
1685
1787
|
var inputStyle = {
|
|
1686
1788
|
flex: 1,
|
|
@@ -2680,7 +2782,7 @@ function Msg({ m, peer, T, lang, onTheater, onDelete, onCancel, onRetry, onRetry
|
|
|
2680
2782
|
) : 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)" })))
|
|
2681
2783
|
);
|
|
2682
2784
|
}
|
|
2683
|
-
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 }) {
|
|
2785
|
+
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 }) {
|
|
2684
2786
|
const scrollRef = React.useRef(null);
|
|
2685
2787
|
const prependRef = React.useRef(null);
|
|
2686
2788
|
const [earlierBusy, setEarlierBusy] = React.useState(false);
|
|
@@ -3102,7 +3204,15 @@ ${peer.address}`
|
|
|
3102
3204
|
fontSize: 12.5,
|
|
3103
3205
|
color: "var(--text)"
|
|
3104
3206
|
} }, /* @__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.")),
|
|
3105
|
-
/* @__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(
|
|
3207
|
+
/* @__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: {
|
|
3208
|
+
fontFamily: "var(--mono)",
|
|
3209
|
+
fontSize: 10.5,
|
|
3210
|
+
color: "var(--faint)",
|
|
3211
|
+
maxWidth: "86%",
|
|
3212
|
+
whiteSpace: "nowrap",
|
|
3213
|
+
overflow: "hidden",
|
|
3214
|
+
textOverflow: "ellipsis"
|
|
3215
|
+
} }, m.line)) : /* @__PURE__ */ React.createElement(
|
|
3106
3216
|
Msg,
|
|
3107
3217
|
{
|
|
3108
3218
|
key: m.id || i,
|
|
@@ -3273,12 +3383,12 @@ function MenuItem({ icon, label, onClick, danger }) {
|
|
|
3273
3383
|
function ChatEmpty({ T }) {
|
|
3274
3384
|
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));
|
|
3275
3385
|
}
|
|
3276
|
-
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 }) {
|
|
3386
|
+
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 }) {
|
|
3277
3387
|
const peer = peers.find((p) => p.id === activeId);
|
|
3278
3388
|
if (phone) {
|
|
3279
|
-
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 });
|
|
3389
|
+
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 });
|
|
3280
3390
|
}
|
|
3281
|
-
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 }));
|
|
3391
|
+
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 }));
|
|
3282
3392
|
}
|
|
3283
3393
|
Object.assign(window, { ChatTab });
|
|
3284
3394
|
|
|
@@ -5922,7 +6032,7 @@ function dkFlashStop() {
|
|
|
5922
6032
|
function dkLooking() {
|
|
5923
6033
|
return document.visibilityState === "visible" && document.hasFocus();
|
|
5924
6034
|
}
|
|
5925
|
-
function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true, flash = true, muted }) {
|
|
6035
|
+
function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true, flash = true, muted, isProtocol }) {
|
|
5926
6036
|
const prev = React.useRef(null);
|
|
5927
6037
|
const mutedRef = React.useRef(/* @__PURE__ */ new Set());
|
|
5928
6038
|
mutedRef.current = new Set(muted || []);
|
|
@@ -5952,7 +6062,8 @@ function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true,
|
|
|
5952
6062
|
const byId = /* @__PURE__ */ new Map();
|
|
5953
6063
|
for (const p of peers || []) {
|
|
5954
6064
|
const dir = p.lastDir || (String(p.lastMsg || "").startsWith("you: ") ? "out" : "in");
|
|
5955
|
-
|
|
6065
|
+
const quiet = typeof isProtocol === "function" && lastIsProtocol(p, isProtocol);
|
|
6066
|
+
byId.set(p.id, { ts: p.lastTs || 0, dir, quiet });
|
|
5956
6067
|
}
|
|
5957
6068
|
const reqIds = new Set((requests || []).map((r) => r.id));
|
|
5958
6069
|
const was = prev.current;
|
|
@@ -5963,6 +6074,8 @@ function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true,
|
|
|
5963
6074
|
for (const [id, cur] of byId) {
|
|
5964
6075
|
if (cur.dir !== "in" || !cur.ts)
|
|
5965
6076
|
continue;
|
|
6077
|
+
if (cur.quiet)
|
|
6078
|
+
continue;
|
|
5966
6079
|
const old = was.byId.get(id);
|
|
5967
6080
|
if (old ? cur.ts <= old.ts : Date.now() - cur.ts > 3e4)
|
|
5968
6081
|
continue;
|
|
@@ -6810,6 +6923,7 @@ function DkApp() {
|
|
|
6810
6923
|
const rowPad = t.density === "comfortable" ? "11px 12px" : "7px 10px";
|
|
6811
6924
|
const mutedIds = Array.isArray(t.alertMuted) ? t.alertMuted : [];
|
|
6812
6925
|
const onToggleMute = (id) => setTweak("alertMuted", mutedIds.includes(id) ? mutedIds.filter((x) => x !== id) : mutedIds.concat(id));
|
|
6926
|
+
const isProtocol = React.useMemo(() => protocolTest(hostIsProtocolMessage), []);
|
|
6813
6927
|
useIncomingAlerts({
|
|
6814
6928
|
T,
|
|
6815
6929
|
peers,
|
|
@@ -6818,7 +6932,8 @@ function DkApp() {
|
|
|
6818
6932
|
activeId,
|
|
6819
6933
|
sound: t.alertSound !== false,
|
|
6820
6934
|
flash: t.alertFlash !== false,
|
|
6821
|
-
muted: mutedIds
|
|
6935
|
+
muted: mutedIds,
|
|
6936
|
+
isProtocol
|
|
6822
6937
|
});
|
|
6823
6938
|
const assistant = useAssistant({
|
|
6824
6939
|
lang: t.lang,
|
|
@@ -6852,6 +6967,54 @@ function DkApp() {
|
|
|
6852
6967
|
});
|
|
6853
6968
|
const phone = useIsPhone();
|
|
6854
6969
|
const [sharing, setSharing] = React.useState(false);
|
|
6970
|
+
const headerActions = (hostHeaderActions || []).filter((a) => a && a.id && typeof a.onClick === "function");
|
|
6971
|
+
const headerActionBtns = headerActions.map((a) => {
|
|
6972
|
+
let on = false;
|
|
6973
|
+
try {
|
|
6974
|
+
on = typeof a.active === "function" ? !!a.active() : false;
|
|
6975
|
+
} catch (e) {
|
|
6976
|
+
on = false;
|
|
6977
|
+
}
|
|
6978
|
+
const label = pickText(a.label, t.lang, a.id);
|
|
6979
|
+
return /* @__PURE__ */ React.createElement(
|
|
6980
|
+
"button",
|
|
6981
|
+
{
|
|
6982
|
+
key: a.id,
|
|
6983
|
+
title: label,
|
|
6984
|
+
"aria-label": label,
|
|
6985
|
+
"aria-pressed": on,
|
|
6986
|
+
onClick: () => {
|
|
6987
|
+
try {
|
|
6988
|
+
a.onClick({ T, t, me, activeId, phone });
|
|
6989
|
+
} catch (e) {
|
|
6990
|
+
}
|
|
6991
|
+
},
|
|
6992
|
+
style: {
|
|
6993
|
+
width: 32,
|
|
6994
|
+
height: 32,
|
|
6995
|
+
flexShrink: 0,
|
|
6996
|
+
borderRadius: 8,
|
|
6997
|
+
cursor: "pointer",
|
|
6998
|
+
padding: 0,
|
|
6999
|
+
border: "1px solid " + (on ? "var(--accent)" : "var(--line)"),
|
|
7000
|
+
background: on ? "color-mix(in oklab, var(--accent), transparent 86%)" : "transparent",
|
|
7001
|
+
display: "flex",
|
|
7002
|
+
alignItems: "center",
|
|
7003
|
+
justifyContent: "center"
|
|
7004
|
+
}
|
|
7005
|
+
},
|
|
7006
|
+
/* @__PURE__ */ React.createElement(
|
|
7007
|
+
Icon,
|
|
7008
|
+
{
|
|
7009
|
+
name: typeof a.icon === "string" ? a.icon : void 0,
|
|
7010
|
+
svgPath: a.icon && typeof a.icon === "object" ? a.icon.svgPath : void 0,
|
|
7011
|
+
size: 16,
|
|
7012
|
+
stroke: 2,
|
|
7013
|
+
color: on ? "var(--accent)" : "var(--dim)"
|
|
7014
|
+
}
|
|
7015
|
+
)
|
|
7016
|
+
);
|
|
7017
|
+
});
|
|
6855
7018
|
const shareBtn = /* @__PURE__ */ React.createElement(
|
|
6856
7019
|
"button",
|
|
6857
7020
|
{
|
|
@@ -7022,7 +7185,7 @@ function DkApp() {
|
|
|
7022
7185
|
const lastPeople = React.useRef("here");
|
|
7023
7186
|
if (peopleTab)
|
|
7024
7187
|
lastPeople.current = tab;
|
|
7025
|
-
const unreadTotal = (peers
|
|
7188
|
+
const unreadTotal = rosterUnreadTotal(peers, isProtocol) + (requests || []).length;
|
|
7026
7189
|
const NAV_FOR = {
|
|
7027
7190
|
chat: { icon: "message", label: T.chat, active: tab === "chat", badge: unreadTotal, go: () => setTab("chat") },
|
|
7028
7191
|
people: { icon: "users", label: T.people, active: peopleTab, go: () => setTab(lastPeople.current) },
|
|
@@ -7066,7 +7229,7 @@ function DkApp() {
|
|
|
7066
7229
|
display: "flex",
|
|
7067
7230
|
border: "1px solid " + (tab === "profile" ? "var(--line)" : "transparent"),
|
|
7068
7231
|
background: tab === "profile" ? "var(--panel-2)" : "transparent"
|
|
7069
|
-
} }, /* @__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: {
|
|
7232
|
+
} }, /* @__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: {
|
|
7070
7233
|
display: "inline-flex",
|
|
7071
7234
|
alignItems: "center",
|
|
7072
7235
|
gap: 6,
|
|
@@ -7080,7 +7243,7 @@ function DkApp() {
|
|
|
7080
7243
|
fontWeight: 600,
|
|
7081
7244
|
background: tab === n.id ? "var(--accent)" : "transparent",
|
|
7082
7245
|
color: tab === n.id ? "#fff" : "var(--dim)"
|
|
7083
|
-
} }, /* @__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(
|
|
7246
|
+
} }, /* @__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(
|
|
7084
7247
|
ProfileTab,
|
|
7085
7248
|
{
|
|
7086
7249
|
T,
|
package/package.json
CHANGED