@decentnetwork/beagle 0.1.112 → 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 +420 -55
- 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
|
|
@@ -68,8 +68,8 @@ window.__DK_UI_VERSION="0.1.112";
|
|
|
68
68
|
speaker: '<path d="M11 5 6 9H3v6h3l5 4V5Z"/><path d="M16 9a3 3 0 0 1 0 6"/><path d="M19 7a6 6 0 0 1 0 10"/>',
|
|
69
69
|
micFill: '<rect x="9" y="2.5" width="6" height="11.5" rx="3"/><path d="M5.5 11a6.5 6.5 0 0 0 13 0"/><path d="M12 17.5v3.5"/>'
|
|
70
70
|
};
|
|
71
|
-
function Icon({ name, size = 24, stroke = 1.75, color = "currentColor", fill = "none", style }) {
|
|
72
|
-
const d = ICON_PATHS[name];
|
|
71
|
+
function Icon({ name, size = 24, stroke = 1.75, color = "currentColor", fill = "none", style, svgPath }) {
|
|
72
|
+
const d = svgPath || ICON_PATHS[name];
|
|
73
73
|
if (!d)
|
|
74
74
|
return null;
|
|
75
75
|
return /* @__PURE__ */ React.createElement(
|
|
@@ -111,7 +111,12 @@ window.__DK_UI_VERSION="0.1.112";
|
|
|
111
111
|
zap: '<path d="M13 2 4 13h7l-1 9 9-11h-7l1-9Z"/>',
|
|
112
112
|
clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/>',
|
|
113
113
|
bellOff: '<path d="M8.7 3.4A6 6 0 0 1 18 8.5c0 3 .5 5 1.1 6.3M6.1 6.1A6 6 0 0 0 6 8.5c0 6.5-2.5 8-2.5 8H16"/><path d="M13.7 20.5a2 2 0 0 1-3.4 0"/><path d="m3 3 18 18"/>',
|
|
114
|
-
plug: '<path d="M9 2v6M15 2v6"/><path d="M7 8h10v3a5 5 0 0 1-10 0V8Z"/><path d="M12 16v6"/>'
|
|
114
|
+
plug: '<path d="M9 2v6M15 2v6"/><path d="M7 8h10v3a5 5 0 0 1-10 0V8Z"/><path d="M12 16v6"/>',
|
|
115
|
+
// A form with a filled-in field: the glyph for "Fill with Beagle" and any
|
|
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"/>',
|
|
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"/>'
|
|
115
120
|
});
|
|
116
121
|
var H = {};
|
|
117
122
|
var hostNeedsWelcome = false;
|
|
@@ -119,8 +124,20 @@ window.__DK_UI_VERSION="0.1.112";
|
|
|
119
124
|
var hostHasKeyBackup = false;
|
|
120
125
|
var hostEnsWrite = true;
|
|
121
126
|
var hostBrandHref = "";
|
|
127
|
+
var hostTabs = null;
|
|
128
|
+
var hostHideApps = null;
|
|
129
|
+
var hostApps = null;
|
|
130
|
+
var hostProfileSections = null;
|
|
131
|
+
var hostHeaderActions = null;
|
|
132
|
+
var hostIsProtocolMessage = null;
|
|
122
133
|
function setUiHost(host2) {
|
|
123
134
|
H = host2 || {};
|
|
135
|
+
hostTabs = Array.isArray(H.hostTabs) ? H.hostTabs : null;
|
|
136
|
+
hostHideApps = Array.isArray(H.hideApps) ? H.hideApps : null;
|
|
137
|
+
hostApps = Array.isArray(H.hostApps) ? H.hostApps : null;
|
|
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;
|
|
124
141
|
hostNeedsWelcome = !!H.hostNeedsWelcome;
|
|
125
142
|
hostPrefersRtcFile = !!H.hostPrefersRtcFile;
|
|
126
143
|
hostHasKeyBackup = !!H.hostHasKeyBackup;
|
|
@@ -1476,6 +1493,98 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
1476
1493
|
}, [append, lang, onReloadFriends]);
|
|
1477
1494
|
return { thread, handle, removeFriends, adoptServicePeers };
|
|
1478
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
|
+
}
|
|
1479
1588
|
var DK_CMD_HIST_KEY = "beagle.assistant.history";
|
|
1480
1589
|
var DK_CMD_HIST_MAX = 50;
|
|
1481
1590
|
function RequestsBlock({ T, requests, onAct }) {
|
|
@@ -1536,8 +1645,9 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
1536
1645
|
paddingLeft: 8
|
|
1537
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))))));
|
|
1538
1647
|
}
|
|
1539
|
-
function PeerRow({ peer, T, active, onClick, muted }) {
|
|
1648
|
+
function PeerRow({ peer, T, active, onClick, muted, isProtocol }) {
|
|
1540
1649
|
const name = peer.alias || peer.userId;
|
|
1650
|
+
const row = rosterRow(peer, isProtocol || (() => false));
|
|
1541
1651
|
return /* @__PURE__ */ React.createElement("button", { onClick, style: {
|
|
1542
1652
|
display: "flex",
|
|
1543
1653
|
alignItems: "center",
|
|
@@ -1566,12 +1676,13 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
1566
1676
|
textOverflow: "ellipsis",
|
|
1567
1677
|
flex: 1,
|
|
1568
1678
|
minWidth: 0
|
|
1569
|
-
} }, 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 })));
|
|
1570
1680
|
}
|
|
1571
|
-
function dkActivityTs(p) {
|
|
1572
|
-
|
|
1681
|
+
function dkActivityTs(p, isProtocol) {
|
|
1682
|
+
const row = rosterRow(p, isProtocol || (() => false));
|
|
1683
|
+
return Math.max(row.activityTs, p.acceptedAt || 0, p.requestedAt || 0);
|
|
1573
1684
|
}
|
|
1574
|
-
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 }) {
|
|
1575
1686
|
const [q, setQ] = React.useState("");
|
|
1576
1687
|
const [addr, setAddr] = React.useState("");
|
|
1577
1688
|
const [addState, setAddState] = React.useState(null);
|
|
@@ -1616,7 +1727,7 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
1616
1727
|
}).slice().sort((a, b) => {
|
|
1617
1728
|
if (!!a.pinned !== !!b.pinned)
|
|
1618
1729
|
return a.pinned ? -1 : 1;
|
|
1619
|
-
const ta = dkActivityTs(a), tb = dkActivityTs(b);
|
|
1730
|
+
const ta = dkActivityTs(a, isProtocol), tb = dkActivityTs(b, isProtocol);
|
|
1620
1731
|
if (ta !== tb)
|
|
1621
1732
|
return tb - ta;
|
|
1622
1733
|
return String(a.alias || a.userId || "").localeCompare(String(b.alias || b.userId || ""));
|
|
@@ -1655,7 +1766,7 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
1655
1766
|
fontSize: 11.5,
|
|
1656
1767
|
lineHeight: 1.35,
|
|
1657
1768
|
color: addState.kind === "err" ? "var(--bad, #e5484d)" : addState.kind === "ok" ? "var(--good, #46a758)" : "var(--faint)"
|
|
1658
|
-
} }, 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"))));
|
|
1659
1770
|
}
|
|
1660
1771
|
var inputStyle = {
|
|
1661
1772
|
flex: 1,
|
|
@@ -2655,7 +2766,7 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
2655
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)" })))
|
|
2656
2767
|
);
|
|
2657
2768
|
}
|
|
2658
|
-
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 }) {
|
|
2659
2770
|
const scrollRef = React.useRef(null);
|
|
2660
2771
|
const prependRef = React.useRef(null);
|
|
2661
2772
|
const [earlierBusy, setEarlierBusy] = React.useState(false);
|
|
@@ -3018,7 +3129,7 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
3018
3129
|
// The internal id is not an address anyone can use and not a name
|
|
3019
3130
|
// anyone chose; saying what this row IS is more use than showing it.
|
|
3020
3131
|
/* @__PURE__ */ React.createElement("div", { style: { marginTop: 2, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, T && T.assistantSub || "Local \xB7 APP")
|
|
3021
|
-
) : phone ? /* @__PURE__ */ React.createElement("div", { style: { marginTop: 2, display: "flex" } }, /* @__PURE__ */ React.createElement(RouteTag, { peer })) : /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 2 } }, /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.userId, title: peer.userId }, shortKey(peer.userId, 10, 6)), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement("button", { onClick: () => onOpenNet(peer), title: T && T.openNet || "Network view", style: { background: "none", border: "none", cursor: "pointer", padding: 0, color: "var(--accent)", display: "inline-flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(Icon, { name: "network", size: 12, stroke: 2 })), /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.ip, title: peer.ip }, peer.ip), peer.address && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement(
|
|
3132
|
+
) : phone ? /* @__PURE__ */ React.createElement("div", { style: { marginTop: 2, display: "flex" } }, /* @__PURE__ */ React.createElement(RouteTag, { peer })) : /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 2 } }, /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.userId, title: peer.userId }, shortKey(peer.userId, 10, 6)), onOpenNet && /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), onOpenNet && /* @__PURE__ */ React.createElement("button", { onClick: () => onOpenNet(peer), title: T && T.openNet || "Network view", style: { background: "none", border: "none", cursor: "pointer", padding: 0, color: "var(--accent)", display: "inline-flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(Icon, { name: "network", size: 12, stroke: 2 })), /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.ip, title: peer.ip }, peer.ip), peer.address && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement(
|
|
3022
3133
|
Mono,
|
|
3023
3134
|
{
|
|
3024
3135
|
size: 11.5,
|
|
@@ -3053,7 +3164,7 @@ ${peer.address}`
|
|
|
3053
3164
|
} }), phone && onSendRtcFile && !peer.app && /* @__PURE__ */ React.createElement(MenuItem, { icon: "file", label: T && T.sendWebrtcFile || "Send file via WebRTC", onClick: () => {
|
|
3054
3165
|
setMenu(false);
|
|
3055
3166
|
rtcFileRef.current && rtcFileRef.current.click();
|
|
3056
|
-
} }), phone && !peer.app && /* @__PURE__ */ React.createElement(MenuItem, { icon: "network", label: T && T.openNet || "Network view", onClick: () => {
|
|
3167
|
+
} }), phone && !peer.app && onOpenNet && /* @__PURE__ */ React.createElement(MenuItem, { icon: "network", label: T && T.openNet || "Network view", onClick: () => {
|
|
3057
3168
|
setMenu(false);
|
|
3058
3169
|
onOpenNet(peer);
|
|
3059
3170
|
} }), !peer.app && /* @__PURE__ */ React.createElement(MenuItem, { icon: "hash", label: T.alias, onClick: () => {
|
|
@@ -3077,7 +3188,15 @@ ${peer.address}`
|
|
|
3077
3188
|
fontSize: 12.5,
|
|
3078
3189
|
color: "var(--text)"
|
|
3079
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.")),
|
|
3080
|
-
/* @__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(
|
|
3081
3200
|
Msg,
|
|
3082
3201
|
{
|
|
3083
3202
|
key: m.id || i,
|
|
@@ -3248,12 +3367,12 @@ ${peer.address}`
|
|
|
3248
3367
|
function ChatEmpty({ T }) {
|
|
3249
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));
|
|
3250
3369
|
}
|
|
3251
|
-
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 }) {
|
|
3252
3371
|
const peer = peers.find((p) => p.id === activeId);
|
|
3253
3372
|
if (phone) {
|
|
3254
|
-
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 });
|
|
3255
3374
|
}
|
|
3256
|
-
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 }));
|
|
3257
3376
|
}
|
|
3258
3377
|
Object.assign(window, { ChatTab });
|
|
3259
3378
|
function CopyBtn({ value, copiedText = "Copied", copyFailedText = "Copy failed", copyTitle = "Copy" }) {
|
|
@@ -4026,7 +4145,7 @@ ${peer.address}`
|
|
|
4026
4145
|
return /* @__PURE__ */ React.createElement(DkPunkPicker, { T, onPick: pickPunk, onClose: () => setPicking(false) });
|
|
4027
4146
|
return /* @__PURE__ */ React.createElement("div", { onClick: onClose, style: { position: "fixed", inset: 0, zIndex: 90, background: "color-mix(in oklab, #000, transparent 38%)", display: "flex", alignItems: "center", justifyContent: "center", padding: 16 } }, /* @__PURE__ */ React.createElement("div", { onClick: (e) => e.stopPropagation(), style: { width: 360, maxWidth: "100%", background: "var(--panel)", border: "1px solid var(--line)", borderRadius: 16, padding: 16, display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 4 } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 40, radius: 10, dot: false }), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 700, color: "var(--text)" } }, zh ? "\u66F4\u6362\u5934\u50CF" : "Change avatar"), /* @__PURE__ */ React.createElement(Btn, { icon: "x", size: "sm", onClick: onClose })), item("sparkles", zh ? "\u9009\u4E00\u4E2A CryptoPunk" : "Choose a CryptoPunk", () => setPicking(true)), canUpload && item("image", zh ? "\u4E0A\u4F20\u7167\u7247" : "Upload a photo", () => fileRef.current && fileRef.current.click()), has && item("trash", zh ? "\u79FB\u9664\u5934\u50CF" : "Remove avatar", () => save(canUpload ? { punkId: null, avatarDataUrl: null } : { punkId: null }), true), /* @__PURE__ */ React.createElement("input", { ref: fileRef, type: "file", accept: "image/*", onChange: onFile, style: { display: "none" } }), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--faint)", lineHeight: 1.5, marginTop: 2 } }, zh ? "\u4E0D\u9700\u8981\u6CE8\u518C beagles.eth \u540D\u5B57\u3002\u597D\u53CB\u4F1A\u5728\u4E0B\u6B21\u6536\u5230\u4F60\u7684\u8D44\u6599\u65F6\u770B\u5230\u65B0\u5934\u50CF\u3002" : "No beagles.eth name needed. Friends see it the next time your profile reaches them."), err && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--danger)" } }, err)));
|
|
4028
4147
|
}
|
|
4029
|
-
function ProfileTab({ T, me, onEdit, t, setTweak, onSetAvatar }) {
|
|
4148
|
+
function ProfileTab({ T, me, onEdit, t, setTweak, onSetAvatar, sections, sectionProps }) {
|
|
4030
4149
|
var _a;
|
|
4031
4150
|
const [avatarSheet, setAvatarSheet] = React.useState(false);
|
|
4032
4151
|
const [qr, setQr] = React.useState(null);
|
|
@@ -4058,12 +4177,33 @@ ${peer.address}`
|
|
|
4058
4177
|
},
|
|
4059
4178
|
style: { width: 16, height: 16, accentColor: "var(--accent)" }
|
|
4060
4179
|
}
|
|
4061
|
-
), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--text)" } }, T && T.autoAcceptLabel || "Auto-accept friend requests"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--dim)", marginTop: 2 } }, T && T.autoAcceptSub || "Off: requests wait in the sidebar for your approval.")))), hostHasKeyBackup && /* @__PURE__ */ React.createElement(DkKeyBackup, { t: t || {} }), t && setTweak && /* @__PURE__ */ React.createElement(DkAlertSettings, { t, setTweak }), t && setTweak && /* @__PURE__ */ React.createElement(DkDisplaySettings, { t, setTweak }), /* @__PURE__ */ React.createElement(Card, { label: T.network }, /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.virtualIp, value: me.ip, copy: true }), /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.wireLabel, value: `${me.wire} \xB7 lossless`, mono: false }), /* @__PURE__ */ React.createElement(DkVersionRow, { T, me }), /* @__PURE__ */ React.createElement(DkUpdateCheck, { T })), /* @__PURE__ */ React.createElement(Card, { label: T.dangerZone }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 13, padding: "14px 16px" } }, /* @__PURE__ */ React.createElement("div", { style: { width: 34, height: 34, borderRadius: 8, flexShrink: 0, background: "color-mix(in oklab, var(--danger), transparent 86%)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--danger)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "trash", size: 17, stroke: 2 })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--danger)" } }, T.deleteNode), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)", marginTop: 1 } }, T.deleteSub)), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", size: "sm" }, T.delete)))), qr && /* @__PURE__ */ React.createElement(DkQrModal, { T, value: qr.value, label: qr.label, onClose: () => setQr(null) }), avatarSheet && /* @__PURE__ */ React.createElement(DkAvatarSheet, { T, me, zh: t && t.lang === "zh", onSet: onSetAvatar, onClose: () => setAvatarSheet(false) }), editing && /* @__PURE__ */ React.createElement(DkEditModal, { T, me, onClose: () => setEditing(false), onSave: (name, description) => {
|
|
4180
|
+
), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--text)" } }, T && T.autoAcceptLabel || "Auto-accept friend requests"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--dim)", marginTop: 2 } }, T && T.autoAcceptSub || "Off: requests wait in the sidebar for your approval.")))), hostHasKeyBackup && /* @__PURE__ */ React.createElement(DkKeyBackup, { t: t || {} }), t && setTweak && /* @__PURE__ */ React.createElement(DkAlertSettings, { t, setTweak }), t && setTweak && /* @__PURE__ */ React.createElement(DkDisplaySettings, { t, setTweak }), /* @__PURE__ */ React.createElement(Card, { label: T.network }, /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.virtualIp, value: me.ip, copy: true }), /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.wireLabel, value: `${me.wire} \xB7 lossless`, mono: false }), /* @__PURE__ */ React.createElement(DkVersionRow, { T, me }), /* @__PURE__ */ React.createElement(DkUpdateCheck, { T })), /* @__PURE__ */ React.createElement(Card, { label: T.dangerZone }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 13, padding: "14px 16px" } }, /* @__PURE__ */ React.createElement("div", { style: { width: 34, height: 34, borderRadius: 8, flexShrink: 0, background: "color-mix(in oklab, var(--danger), transparent 86%)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--danger)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "trash", size: 17, stroke: 2 })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--danger)" } }, T.deleteNode), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)", marginTop: 1 } }, T.deleteSub)), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", size: "sm" }, T.delete)))), Array.isArray(sections) && sections.length > 0 && /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto", padding: "0 20px 40px", display: "flex", flexDirection: "column", gap: 16 } }, sections.map((sec) => sec && typeof sec.render === "function" ? /* @__PURE__ */ React.createElement(DkHostSection, { key: sec.id, sec, props: sectionProps, lang: t && t.lang }) : null)), qr && /* @__PURE__ */ React.createElement(DkQrModal, { T, value: qr.value, label: qr.label, onClose: () => setQr(null) }), avatarSheet && /* @__PURE__ */ React.createElement(DkAvatarSheet, { T, me, zh: t && t.lang === "zh", onSet: onSetAvatar, onClose: () => setAvatarSheet(false) }), editing && /* @__PURE__ */ React.createElement(DkEditModal, { T, me, onClose: () => setEditing(false), onSave: (name, description) => {
|
|
4062
4181
|
if (onEdit)
|
|
4063
4182
|
onEdit(name, description);
|
|
4064
4183
|
setEditing(false);
|
|
4065
4184
|
} }));
|
|
4066
4185
|
}
|
|
4186
|
+
var DkHostSection = class extends React.Component {
|
|
4187
|
+
constructor(props) {
|
|
4188
|
+
super(props);
|
|
4189
|
+
this.state = { err: null };
|
|
4190
|
+
}
|
|
4191
|
+
static getDerivedStateFromError(err) {
|
|
4192
|
+
return { err };
|
|
4193
|
+
}
|
|
4194
|
+
render() {
|
|
4195
|
+
var _a;
|
|
4196
|
+
const { sec, props, lang } = this.props;
|
|
4197
|
+
if (this.state.err) {
|
|
4198
|
+
return /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)" } }, lang === "zh" ? "\u8FD9\u4E2A\u9644\u52A0\u677F\u5757\u51FA\u9519\u4E86\u3002" : "This host section failed to render.");
|
|
4199
|
+
}
|
|
4200
|
+
try {
|
|
4201
|
+
return (_a = sec.render(props)) != null ? _a : null;
|
|
4202
|
+
} catch (e) {
|
|
4203
|
+
return null;
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
};
|
|
4067
4207
|
Object.assign(window, { DkShareSheet, ProfileTab, DkPunkAvatar, DkEnsAvatar, DkPublicProfile });
|
|
4068
4208
|
var DK_PHONE_MQ = "(max-width: 700px)";
|
|
4069
4209
|
function useIsPhone() {
|
|
@@ -4277,6 +4417,82 @@ ${peer.address}`
|
|
|
4277
4417
|
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "0 2px", ...style } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, letterSpacing: 1, textTransform: "uppercase", color: "var(--faint)" } }, label), count != null && /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, color: "var(--faint)" } }, count), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, height: 1, background: "var(--line)" } }), trailing);
|
|
4278
4418
|
}
|
|
4279
4419
|
Object.assign(window, { useIsPhone, DkPhoneStyles, DkIdenticon, DkBot, DkAvatar, DkImgAvatar, StatusDot, Mono, shortKey, RouteTag, Tag, Unread, Btn, Section });
|
|
4420
|
+
var BUILTIN_TABS = ["chat", "people", "network", "apps", "profile"];
|
|
4421
|
+
var warned = /* @__PURE__ */ new Set();
|
|
4422
|
+
function warnOnce(what) {
|
|
4423
|
+
if (warned.has(what))
|
|
4424
|
+
return;
|
|
4425
|
+
warned.add(what);
|
|
4426
|
+
try {
|
|
4427
|
+
console.warn(`[beagle-ui] ignoring unknown host tab or app: ${what}`);
|
|
4428
|
+
} catch (e) {
|
|
4429
|
+
}
|
|
4430
|
+
}
|
|
4431
|
+
function pickText(value, lang, fallback = "") {
|
|
4432
|
+
if (typeof value === "string")
|
|
4433
|
+
return value;
|
|
4434
|
+
if (value && typeof value === "object")
|
|
4435
|
+
return value[lang] || value.en || value.zh || fallback;
|
|
4436
|
+
return fallback;
|
|
4437
|
+
}
|
|
4438
|
+
function resolveTabs(hostTabs2) {
|
|
4439
|
+
if (!Array.isArray(hostTabs2) || !hostTabs2.length) {
|
|
4440
|
+
return BUILTIN_TABS.map((id) => ({ id, kind: "builtin" }));
|
|
4441
|
+
}
|
|
4442
|
+
const out = [];
|
|
4443
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4444
|
+
for (const entry of hostTabs2) {
|
|
4445
|
+
if (typeof entry === "string") {
|
|
4446
|
+
if (!BUILTIN_TABS.includes(entry)) {
|
|
4447
|
+
warnOnce(entry);
|
|
4448
|
+
continue;
|
|
4449
|
+
}
|
|
4450
|
+
if (seen.has(entry))
|
|
4451
|
+
continue;
|
|
4452
|
+
seen.add(entry);
|
|
4453
|
+
out.push({ id: entry, kind: "builtin" });
|
|
4454
|
+
continue;
|
|
4455
|
+
}
|
|
4456
|
+
if (entry && typeof entry === "object" && typeof entry.id === "string" && entry.id) {
|
|
4457
|
+
if (typeof entry.render !== "function") {
|
|
4458
|
+
warnOnce(`${entry.id} (no render function)`);
|
|
4459
|
+
continue;
|
|
4460
|
+
}
|
|
4461
|
+
if (BUILTIN_TABS.includes(entry.id)) {
|
|
4462
|
+
warnOnce(`${entry.id} (a built-in id cannot be redefined)`);
|
|
4463
|
+
continue;
|
|
4464
|
+
}
|
|
4465
|
+
if (seen.has(entry.id))
|
|
4466
|
+
continue;
|
|
4467
|
+
seen.add(entry.id);
|
|
4468
|
+
out.push({
|
|
4469
|
+
id: entry.id,
|
|
4470
|
+
kind: "host",
|
|
4471
|
+
label: entry.label,
|
|
4472
|
+
icon: entry.icon,
|
|
4473
|
+
render: entry.render,
|
|
4474
|
+
badge: typeof entry.badge === "function" ? entry.badge : void 0
|
|
4475
|
+
});
|
|
4476
|
+
continue;
|
|
4477
|
+
}
|
|
4478
|
+
warnOnce(String(entry && entry.id ? entry.id : entry));
|
|
4479
|
+
}
|
|
4480
|
+
return out.length ? out : BUILTIN_TABS.map((id) => ({ id, kind: "builtin" }));
|
|
4481
|
+
}
|
|
4482
|
+
function hasTab(tabs, id) {
|
|
4483
|
+
return (tabs != null ? tabs : []).some((t) => t.id === id);
|
|
4484
|
+
}
|
|
4485
|
+
function resolveApps(builtins, hideApps, hostApps2) {
|
|
4486
|
+
const hidden = new Set(Array.isArray(hideApps) ? hideApps : []);
|
|
4487
|
+
const host2 = (Array.isArray(hostApps2) ? hostApps2 : []).filter((a) => {
|
|
4488
|
+
if (a && typeof a.id === "string" && a.id && typeof a.open === "function")
|
|
4489
|
+
return true;
|
|
4490
|
+
warnOnce(`app ${a && a.id ? a.id : String(a)}`);
|
|
4491
|
+
return false;
|
|
4492
|
+
}).map((a) => ({ ...a, host: true }));
|
|
4493
|
+
const kept = (builtins != null ? builtins : []).filter((a) => !hidden.has(a.id));
|
|
4494
|
+
return host2.concat(kept);
|
|
4495
|
+
}
|
|
4280
4496
|
var APPS = [
|
|
4281
4497
|
{
|
|
4282
4498
|
id: "meet",
|
|
@@ -4316,11 +4532,24 @@ ${peer.address}`
|
|
|
4316
4532
|
const b64 = btoa(unescape(encodeURIComponent(json)));
|
|
4317
4533
|
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
4318
4534
|
}
|
|
4319
|
-
function AppsTab({ T, t }) {
|
|
4535
|
+
function AppsTab({ T, t, me, peers, phone, setTab }) {
|
|
4320
4536
|
const zh = t && t.lang === "zh";
|
|
4537
|
+
const apps = React.useMemo(
|
|
4538
|
+
() => resolveApps(APPS, hostHideApps, hostApps),
|
|
4539
|
+
[]
|
|
4540
|
+
);
|
|
4321
4541
|
const [busy, setBusy] = React.useState(null);
|
|
4322
4542
|
const [err, setErr] = React.useState(null);
|
|
4323
4543
|
const open = async (app) => {
|
|
4544
|
+
if (app.host) {
|
|
4545
|
+
setErr(null);
|
|
4546
|
+
try {
|
|
4547
|
+
app.open({ T, t, me, peers, phone, setTab });
|
|
4548
|
+
} catch (e) {
|
|
4549
|
+
setErr(String(e && e.message || e));
|
|
4550
|
+
}
|
|
4551
|
+
return;
|
|
4552
|
+
}
|
|
4324
4553
|
setBusy(app.id);
|
|
4325
4554
|
setErr(null);
|
|
4326
4555
|
const win = window.open("", "_blank");
|
|
@@ -4342,7 +4571,7 @@ ${peer.address}`
|
|
|
4342
4571
|
setBusy(null);
|
|
4343
4572
|
}
|
|
4344
4573
|
};
|
|
4345
|
-
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 16 } }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, letterSpacing: 0.6, textTransform: "uppercase", color: "var(--faint)" } }, zh ? "\u5E94\u7528" : "Apps"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", marginTop: 6, lineHeight: 1.6 } }, zh ? "\u8FD9\u4E9B\u5E94\u7528\u63A5\u53D7 Beagle \u8EAB\u4EFD\u3002\u4ECE\u8FD9\u91CC\u6253\u5F00\u4E0D\u9700\u8981\u518D\u767B\u5F55\u4E00\u6B21 \u2014\u2014 \u4F1A\u5E26\u4E0A\u4E00\u4E2A\u4E24\u5206\u949F\u5185\u6709\u6548\u7684\u7B7E\u540D\u3002" : "These accept a Beagle identity. Opening one from here does not ask you to sign in again \u2014 it carries a signature that is good for two minutes.")), err && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "#f85149" } }, err),
|
|
4574
|
+
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 16 } }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, letterSpacing: 0.6, textTransform: "uppercase", color: "var(--faint)" } }, zh ? "\u5E94\u7528" : "Apps"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", marginTop: 6, lineHeight: 1.6 } }, zh ? "\u8FD9\u4E9B\u5E94\u7528\u63A5\u53D7 Beagle \u8EAB\u4EFD\u3002\u4ECE\u8FD9\u91CC\u6253\u5F00\u4E0D\u9700\u8981\u518D\u767B\u5F55\u4E00\u6B21 \u2014\u2014 \u4F1A\u5E26\u4E0A\u4E00\u4E2A\u4E24\u5206\u949F\u5185\u6709\u6548\u7684\u7B7E\u540D\u3002" : "These accept a Beagle identity. Opening one from here does not ask you to sign in again \u2014 it carries a signature that is good for two minutes.")), err && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "#f85149" } }, err), apps.map((app) => /* @__PURE__ */ React.createElement("div", { key: app.id, style: {
|
|
4346
4575
|
display: "flex",
|
|
4347
4576
|
alignItems: "center",
|
|
4348
4577
|
gap: 14,
|
|
@@ -4359,7 +4588,7 @@ ${peer.address}`
|
|
|
4359
4588
|
display: "flex",
|
|
4360
4589
|
alignItems: "center",
|
|
4361
4590
|
justifyContent: "center"
|
|
4362
|
-
} }, /* @__PURE__ */ React.createElement(Icon, { name: app.icon, size: 19, color: "var(--accent)", stroke: 1.8 })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 14, fontWeight: 600, color: "var(--text)" } }, app.name), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", marginTop: 2 } },
|
|
4591
|
+
} }, /* @__PURE__ */ React.createElement(Icon, { name: app.icon, size: 19, color: "var(--accent)", stroke: 1.8 })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 14, fontWeight: 600, color: "var(--text)" } }, pickText(app.name, t && t.lang, app.id)), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", marginTop: 2 } }, pickText(app.blurb, t && t.lang, "")), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--dim)", marginTop: 3 } }, app.host ? zh ? "\u672C\u673A\u5E94\u7528" : "runs in this app" : String(app.origin || "").replace(/^https?:\/\//, ""))), /* @__PURE__ */ React.createElement(Btn, { tone: "accent", size: "sm", onClick: busy ? void 0 : () => open(app) }, busy === app.id ? zh ? "\u6253\u5F00\u4E2D\u2026" : "opening\u2026" : zh ? "\u6253\u5F00" : "Open")))));
|
|
4363
4592
|
}
|
|
4364
4593
|
var DK_FALLBACK_ICE_SERVERS = [
|
|
4365
4594
|
{ urls: "stun:stun.l.google.com:19302" },
|
|
@@ -5767,7 +5996,7 @@ ${peer.address}`
|
|
|
5767
5996
|
function dkLooking() {
|
|
5768
5997
|
return document.visibilityState === "visible" && document.hasFocus();
|
|
5769
5998
|
}
|
|
5770
|
-
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 }) {
|
|
5771
6000
|
const prev = React.useRef(null);
|
|
5772
6001
|
const mutedRef = React.useRef(/* @__PURE__ */ new Set());
|
|
5773
6002
|
mutedRef.current = new Set(muted || []);
|
|
@@ -5797,7 +6026,8 @@ ${peer.address}`
|
|
|
5797
6026
|
const byId = /* @__PURE__ */ new Map();
|
|
5798
6027
|
for (const p of peers || []) {
|
|
5799
6028
|
const dir = p.lastDir || (String(p.lastMsg || "").startsWith("you: ") ? "out" : "in");
|
|
5800
|
-
|
|
6029
|
+
const quiet = typeof isProtocol === "function" && lastIsProtocol(p, isProtocol);
|
|
6030
|
+
byId.set(p.id, { ts: p.lastTs || 0, dir, quiet });
|
|
5801
6031
|
}
|
|
5802
6032
|
const reqIds = new Set((requests || []).map((r) => r.id));
|
|
5803
6033
|
const was = prev.current;
|
|
@@ -5808,6 +6038,8 @@ ${peer.address}`
|
|
|
5808
6038
|
for (const [id, cur] of byId) {
|
|
5809
6039
|
if (cur.dir !== "in" || !cur.ts)
|
|
5810
6040
|
continue;
|
|
6041
|
+
if (cur.quiet)
|
|
6042
|
+
continue;
|
|
5811
6043
|
const old = was.byId.get(id);
|
|
5812
6044
|
if (old ? cur.ts <= old.ts : Date.now() - cur.ts > 3e4)
|
|
5813
6045
|
continue;
|
|
@@ -6348,7 +6580,7 @@ ${peer.address}`
|
|
|
6348
6580
|
meetingDesc: "\u8DE8\u670D\u52A1\u5668\u521B\u5EFA\u97F3\u89C6\u9891\u623F\u95F4\uFF0C\u901A\u8FC7\u865A\u62DF IP \u9080\u8BF7\u597D\u53CB\u3002\u7FA4\u7EC4\u901A\u8BDD\u3001\u5C4F\u5E55\u5171\u4EAB\u4E0E\u5171\u4EAB\u51FA\u53E3\u8DEF\u7531\u5C06\u5728\u4E0B\u4E2A\u7248\u672C\u4E0A\u7EBF\u3002"
|
|
6349
6581
|
}
|
|
6350
6582
|
};
|
|
6351
|
-
function RailBtn({ icon, label, active, soon, onClick }) {
|
|
6583
|
+
function RailBtn({ icon, label, active, soon, onClick, svgPath }) {
|
|
6352
6584
|
return /* @__PURE__ */ React.createElement("button", { onClick, title: label, style: {
|
|
6353
6585
|
width: 52,
|
|
6354
6586
|
height: 52,
|
|
@@ -6363,9 +6595,9 @@ ${peer.address}`
|
|
|
6363
6595
|
alignItems: "center",
|
|
6364
6596
|
justifyContent: "center",
|
|
6365
6597
|
gap: 3
|
|
6366
|
-
} }, /* @__PURE__ */ React.createElement(Icon, { name: icon, size: 20, stroke: active ? 2.1 : 1.8, color: active ? "var(--accent)" : "var(--dim)" }), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 9, fontWeight: 600, letterSpacing: 0.2, color: active ? "var(--accent)" : "var(--faint)" } }, label), soon && /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", top: 5, right: 6, width: 6, height: 6, borderRadius: 999, background: "var(--warn)" } }));
|
|
6598
|
+
} }, /* @__PURE__ */ React.createElement(Icon, { name: icon, svgPath, size: 20, stroke: active ? 2.1 : 1.8, color: active ? "var(--accent)" : "var(--dim)" }), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 9, fontWeight: 600, letterSpacing: 0.2, color: active ? "var(--accent)" : "var(--faint)" } }, label), soon && /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", top: 5, right: 6, width: 6, height: 6, borderRadius: 999, background: "var(--warn)" } }));
|
|
6367
6599
|
}
|
|
6368
|
-
function TabBarBtn({ icon, label, active, badge, onClick }) {
|
|
6600
|
+
function TabBarBtn({ icon, label, active, badge, onClick, svgPath }) {
|
|
6369
6601
|
return /* @__PURE__ */ React.createElement("button", { onClick, style: {
|
|
6370
6602
|
flex: 1,
|
|
6371
6603
|
minWidth: 0,
|
|
@@ -6380,9 +6612,36 @@ ${peer.address}`
|
|
|
6380
6612
|
justifyContent: "center",
|
|
6381
6613
|
gap: 3,
|
|
6382
6614
|
fontFamily: "var(--mono)"
|
|
6383
|
-
} }, /* @__PURE__ */ React.createElement(Icon, { name: icon, size: 22, stroke: active ? 2.1 : 1.8, color: active ? "var(--accent)" : "var(--dim)" }), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 10, fontWeight: 600, color: active ? "var(--accent)" : "var(--faint)", whiteSpace: "nowrap" } }, label), badge ? /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", top: 4, left: "50%", marginLeft: 6 } }, /* @__PURE__ */ React.createElement(Unread, { n: badge })) : null);
|
|
6615
|
+
} }, /* @__PURE__ */ React.createElement(Icon, { name: icon, svgPath, size: 22, stroke: active ? 2.1 : 1.8, color: active ? "var(--accent)" : "var(--dim)" }), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 10, fontWeight: 600, color: active ? "var(--accent)" : "var(--faint)", whiteSpace: "nowrap" } }, label), badge ? /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", top: 4, left: "50%", marginLeft: 6 } }, /* @__PURE__ */ React.createElement(Unread, { n: badge })) : null);
|
|
6384
6616
|
}
|
|
6385
6617
|
var DK_PEOPLE_TABS = ["here", "recommended", "registered"];
|
|
6618
|
+
var HostTabPane = class extends React.Component {
|
|
6619
|
+
constructor(props) {
|
|
6620
|
+
super(props);
|
|
6621
|
+
this.state = { err: null };
|
|
6622
|
+
}
|
|
6623
|
+
static getDerivedStateFromError(err) {
|
|
6624
|
+
return { err };
|
|
6625
|
+
}
|
|
6626
|
+
componentDidCatch(err) {
|
|
6627
|
+
try {
|
|
6628
|
+
console.error("[beagle-ui] host tab failed", err);
|
|
6629
|
+
} catch (e) {
|
|
6630
|
+
}
|
|
6631
|
+
}
|
|
6632
|
+
render() {
|
|
6633
|
+
var _a;
|
|
6634
|
+
const { tab, props, lang } = this.props;
|
|
6635
|
+
if (this.state.err) {
|
|
6636
|
+
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: 24, textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--faint)", maxWidth: 420 } }, lang === "zh" ? `"${pickText(tab.label, lang, tab.id)}" \u8FD9\u4E2A\u6807\u7B7E\u9875\u51FA\u9519\u4E86\u3002\u5176\u5B83\u6807\u7B7E\u9875\u4E0D\u53D7\u5F71\u54CD\u3002` : `The "${pickText(tab.label, lang, tab.id)}" tab failed to render. The rest of the app is unaffected.`));
|
|
6637
|
+
}
|
|
6638
|
+
try {
|
|
6639
|
+
return (_a = tab.render(props)) != null ? _a : null;
|
|
6640
|
+
} catch (e) {
|
|
6641
|
+
return null;
|
|
6642
|
+
}
|
|
6643
|
+
}
|
|
6644
|
+
};
|
|
6386
6645
|
function Brand({ T, children, style }) {
|
|
6387
6646
|
if (!hostBrandHref)
|
|
6388
6647
|
return /* @__PURE__ */ React.createElement("span", { style }, children);
|
|
@@ -6499,7 +6758,17 @@ ${peer.address}`
|
|
|
6499
6758
|
}
|
|
6500
6759
|
function DkApp() {
|
|
6501
6760
|
const [t, setTweak] = useTweaks({ ...DK_DEFAULTS, lang: dkDetectLang() });
|
|
6502
|
-
const
|
|
6761
|
+
const tabs = React.useMemo(() => resolveTabs(hostTabs), []);
|
|
6762
|
+
const showPeople = hasTab(tabs, "people");
|
|
6763
|
+
const showNetwork = hasTab(tabs, "network");
|
|
6764
|
+
const showProfile = hasTab(tabs, "profile");
|
|
6765
|
+
const firstTab = tabs[0] ? tabs[0].id === "people" ? "here" : tabs[0].id : "chat";
|
|
6766
|
+
const [tab, setTab] = React.useState(() => {
|
|
6767
|
+
const want = t.startTab || "chat";
|
|
6768
|
+
if (want === "chat" && hasTab(tabs, "chat"))
|
|
6769
|
+
return "chat";
|
|
6770
|
+
return hasTab(tabs, want) || DK_PEOPLE_TABS.includes(want) && showPeople ? want : firstTab;
|
|
6771
|
+
});
|
|
6503
6772
|
const [activeId, setActiveId] = React.useState(null);
|
|
6504
6773
|
const data = useDaemonData();
|
|
6505
6774
|
const me = data.me;
|
|
@@ -6534,11 +6803,13 @@ ${peer.address}`
|
|
|
6534
6803
|
});
|
|
6535
6804
|
}, []);
|
|
6536
6805
|
React.useEffect(() => {
|
|
6806
|
+
if (!showNetwork)
|
|
6807
|
+
return void 0;
|
|
6537
6808
|
refreshBackend();
|
|
6538
6809
|
const ms = backend && backend.state === "releasing" ? 1500 : 8e3;
|
|
6539
6810
|
const t2 = setInterval(refreshBackend, ms);
|
|
6540
6811
|
return () => clearInterval(t2);
|
|
6541
|
-
}, [refreshBackend, backend && backend.state]);
|
|
6812
|
+
}, [refreshBackend, backend && backend.state, showNetwork]);
|
|
6542
6813
|
const armLan = React.useCallback(async () => {
|
|
6543
6814
|
await dkPost("/api/lan-arm", {});
|
|
6544
6815
|
refreshBackend();
|
|
@@ -6612,6 +6883,7 @@ ${peer.address}`
|
|
|
6612
6883
|
const rowPad = t.density === "comfortable" ? "11px 12px" : "7px 10px";
|
|
6613
6884
|
const mutedIds = Array.isArray(t.alertMuted) ? t.alertMuted : [];
|
|
6614
6885
|
const onToggleMute = (id) => setTweak("alertMuted", mutedIds.includes(id) ? mutedIds.filter((x) => x !== id) : mutedIds.concat(id));
|
|
6886
|
+
const isProtocol = React.useMemo(() => protocolTest(hostIsProtocolMessage), []);
|
|
6615
6887
|
useIncomingAlerts({
|
|
6616
6888
|
T,
|
|
6617
6889
|
peers,
|
|
@@ -6620,7 +6892,8 @@ ${peer.address}`
|
|
|
6620
6892
|
activeId,
|
|
6621
6893
|
sound: t.alertSound !== false,
|
|
6622
6894
|
flash: t.alertFlash !== false,
|
|
6623
|
-
muted: mutedIds
|
|
6895
|
+
muted: mutedIds,
|
|
6896
|
+
isProtocol
|
|
6624
6897
|
});
|
|
6625
6898
|
const assistant = useAssistant({
|
|
6626
6899
|
lang: t.lang,
|
|
@@ -6654,6 +6927,54 @@ ${peer.address}`
|
|
|
6654
6927
|
});
|
|
6655
6928
|
const phone = useIsPhone();
|
|
6656
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
|
+
});
|
|
6657
6978
|
const shareBtn = /* @__PURE__ */ React.createElement(
|
|
6658
6979
|
"button",
|
|
6659
6980
|
{
|
|
@@ -6807,36 +7128,68 @@ ${peer.address}`
|
|
|
6807
7128
|
setTab("chat");
|
|
6808
7129
|
openConversation(id);
|
|
6809
7130
|
};
|
|
6810
|
-
const onOpenNet = () => setTab("network");
|
|
6811
|
-
const
|
|
6812
|
-
{ id: "chat", icon: "message", label: T.chat },
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
7131
|
+
const onOpenNet = showNetwork ? () => setTab("network") : null;
|
|
7132
|
+
const RAIL_FOR = {
|
|
7133
|
+
chat: [{ id: "chat", icon: "message", label: T.chat }],
|
|
7134
|
+
people: [
|
|
7135
|
+
{ id: "here", icon: "users", label: T.here },
|
|
7136
|
+
{ id: "recommended", icon: "sparkles", label: T.recommended },
|
|
7137
|
+
{ id: "registered", icon: "at", label: T.registered }
|
|
7138
|
+
],
|
|
7139
|
+
network: [{ id: "network", icon: "network", label: T.network }],
|
|
7140
|
+
apps: [{ id: "apps", icon: "grid", label: T.apps }],
|
|
7141
|
+
profile: []
|
|
7142
|
+
};
|
|
7143
|
+
const nav = tabs.flatMap((x) => x.kind === "builtin" ? RAIL_FOR[x.id] || [] : [{ id: x.id, icon: x.icon, label: pickText(x.label, t.lang, x.id) }]);
|
|
6819
7144
|
const peopleTab = DK_PEOPLE_TABS.includes(tab);
|
|
6820
7145
|
const lastPeople = React.useRef("here");
|
|
6821
7146
|
if (peopleTab)
|
|
6822
7147
|
lastPeople.current = tab;
|
|
6823
|
-
const unreadTotal = (peers
|
|
6824
|
-
const
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
7148
|
+
const unreadTotal = rosterUnreadTotal(peers, isProtocol) + (requests || []).length;
|
|
7149
|
+
const NAV_FOR = {
|
|
7150
|
+
chat: { icon: "message", label: T.chat, active: tab === "chat", badge: unreadTotal, go: () => setTab("chat") },
|
|
7151
|
+
people: { icon: "users", label: T.people, active: peopleTab, go: () => setTab(lastPeople.current) },
|
|
7152
|
+
network: { icon: "network", label: T.network, active: tab === "network", go: () => setTab("network") },
|
|
7153
|
+
apps: { icon: "grid", label: T.apps, active: tab === "apps", go: () => setTab("apps") },
|
|
7154
|
+
profile: { icon: "userRound", label: T.profile, active: tab === "profile", go: () => setTab("profile") }
|
|
7155
|
+
};
|
|
7156
|
+
const phoneNav = tabs.map((x) => x.kind === "builtin" ? { id: x.id, ...NAV_FOR[x.id] } : {
|
|
7157
|
+
id: x.id,
|
|
7158
|
+
icon: typeof x.icon === "string" ? x.icon : void 0,
|
|
7159
|
+
svgPath: x.icon && typeof x.icon === "object" ? x.icon.svgPath : void 0,
|
|
7160
|
+
label: pickText(x.label, t.lang, x.id),
|
|
7161
|
+
active: tab === x.id,
|
|
7162
|
+
// A host badge is called on every render, like the unread count beside
|
|
7163
|
+
// it; a throwing one must not take the tab bar down with it.
|
|
7164
|
+
badge: (() => {
|
|
7165
|
+
try {
|
|
7166
|
+
return x.badge ? x.badge() : 0;
|
|
7167
|
+
} catch (e) {
|
|
7168
|
+
return 0;
|
|
7169
|
+
}
|
|
7170
|
+
})(),
|
|
7171
|
+
go: () => setTab(x.id)
|
|
7172
|
+
});
|
|
6831
7173
|
const inConversation = phone && tab === "chat" && !!activeId && (peers || []).some((p) => p.id === activeId);
|
|
6832
|
-
return /* @__PURE__ */ React.createElement("div", { style: { ...vars, "--row-pad": rowPad, position: "fixed", inset: 0, display: "flex", flexDirection: phone ? "column" : "row", background: "var(--bg)", color: "var(--text)", fontFamily: "var(--ui)" } }, /* @__PURE__ */ React.createElement(DkPhoneStyles, null), !phone && /* @__PURE__ */ React.createElement("div", { style: { width: 68, flexShrink: 0, borderRight: "1px solid var(--line)", background: "var(--rail)", display: "flex", flexDirection: "column", alignItems: "center", padding: "14px 0", gap: 8 } }, /* @__PURE__ */ React.createElement(Brand, { T, style: { width: 38, height: 38, borderRadius: 10, background: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: 8, flexShrink: 0 } }, /* @__PURE__ */ React.createElement(Icon, { name: "terminal", size: 20, color: "#fff", stroke: 2.2 })), nav.map((n) => /* @__PURE__ */ React.createElement(
|
|
7174
|
+
return /* @__PURE__ */ React.createElement("div", { style: { ...vars, "--row-pad": rowPad, position: "fixed", inset: 0, display: "flex", flexDirection: phone ? "column" : "row", background: "var(--bg)", color: "var(--text)", fontFamily: "var(--ui)" } }, /* @__PURE__ */ React.createElement(DkPhoneStyles, null), !phone && /* @__PURE__ */ React.createElement("div", { style: { width: 68, flexShrink: 0, borderRight: "1px solid var(--line)", background: "var(--rail)", display: "flex", flexDirection: "column", alignItems: "center", padding: "14px 0", gap: 8 } }, /* @__PURE__ */ React.createElement(Brand, { T, style: { width: 38, height: 38, borderRadius: 10, background: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: 8, flexShrink: 0 } }, /* @__PURE__ */ React.createElement(Icon, { name: "terminal", size: 20, color: "#fff", stroke: 2.2 })), nav.map((n) => /* @__PURE__ */ React.createElement(
|
|
7175
|
+
RailBtn,
|
|
7176
|
+
{
|
|
7177
|
+
key: n.id,
|
|
7178
|
+
icon: typeof n.icon === "string" ? n.icon : void 0,
|
|
7179
|
+
svgPath: n.icon && typeof n.icon === "object" ? n.icon.svgPath : void 0,
|
|
7180
|
+
label: n.label,
|
|
7181
|
+
active: tab === n.id,
|
|
7182
|
+
soon: n.soon,
|
|
7183
|
+
onClick: () => setTab(n.id)
|
|
7184
|
+
}
|
|
7185
|
+
)), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), showProfile && /* @__PURE__ */ React.createElement("button", { onClick: () => setTab("profile"), title: T.profile, style: {
|
|
6833
7186
|
padding: 4,
|
|
6834
7187
|
borderRadius: 12,
|
|
6835
7188
|
cursor: "pointer",
|
|
6836
7189
|
display: "flex",
|
|
6837
7190
|
border: "1px solid " + (tab === "profile" ? "var(--line)" : "transparent"),
|
|
6838
7191
|
background: tab === "profile" ? "var(--panel-2)" : "transparent"
|
|
6839
|
-
} }, /* @__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: {
|
|
6840
7193
|
display: "inline-flex",
|
|
6841
7194
|
alignItems: "center",
|
|
6842
7195
|
gap: 6,
|
|
@@ -6850,10 +7203,22 @@ ${peer.address}`
|
|
|
6850
7203
|
fontWeight: 600,
|
|
6851
7204
|
background: tab === n.id ? "var(--accent)" : "transparent",
|
|
6852
7205
|
color: tab === n.id ? "#fff" : "var(--dim)"
|
|
6853
|
-
} }, /* @__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 }),
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
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(
|
|
7207
|
+
ProfileTab,
|
|
7208
|
+
{
|
|
7209
|
+
T,
|
|
7210
|
+
me,
|
|
7211
|
+
onEdit,
|
|
7212
|
+
t,
|
|
7213
|
+
setTweak,
|
|
7214
|
+
onSetAvatar: (p) => dkApi.setProfileFull(p).then((r) => {
|
|
7215
|
+
data.refresh();
|
|
7216
|
+
return r;
|
|
7217
|
+
}),
|
|
7218
|
+
sections: hostProfileSections,
|
|
7219
|
+
sectionProps: { T, t, me, peers, phone, setTab }
|
|
7220
|
+
}
|
|
7221
|
+
)), phone && !inConversation && /* @__PURE__ */ React.createElement("div", { style: { flexShrink: 0, display: "flex", borderTop: "1px solid var(--line)", background: "var(--rail)", paddingBottom: "env(safe-area-inset-bottom, 0px)" } }, phoneNav.map((n) => /* @__PURE__ */ React.createElement(TabBarBtn, { key: n.id, icon: n.icon, svgPath: n.svgPath, label: n.label, active: n.active, badge: n.badge, onClick: n.go }))), data.locked && /* @__PURE__ */ React.createElement(
|
|
6857
7222
|
DkLockedOut,
|
|
6858
7223
|
{
|
|
6859
7224
|
compact: true,
|
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",
|