@decentnetwork/beagle-ui 0.2.26 → 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 +424 -55
- 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) ----
|
|
@@ -66,8 +66,8 @@ var ICON_PATHS = {
|
|
|
66
66
|
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"/>',
|
|
67
67
|
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"/>'
|
|
68
68
|
};
|
|
69
|
-
function Icon({ name, size = 24, stroke = 1.75, color = "currentColor", fill = "none", style }) {
|
|
70
|
-
const d = ICON_PATHS[name];
|
|
69
|
+
function Icon({ name, size = 24, stroke = 1.75, color = "currentColor", fill = "none", style, svgPath }) {
|
|
70
|
+
const d = svgPath || ICON_PATHS[name];
|
|
71
71
|
if (!d)
|
|
72
72
|
return null;
|
|
73
73
|
return /* @__PURE__ */ React.createElement(
|
|
@@ -111,7 +111,12 @@ Object.assign(ICON_PATHS, {
|
|
|
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
|
|
|
117
122
|
// src/ui-host.jsx
|
|
@@ -121,8 +126,20 @@ var hostPrefersRtcFile = false;
|
|
|
121
126
|
var hostHasKeyBackup = false;
|
|
122
127
|
var hostEnsWrite = true;
|
|
123
128
|
var hostBrandHref = "";
|
|
129
|
+
var hostTabs = null;
|
|
130
|
+
var hostHideApps = null;
|
|
131
|
+
var hostApps = null;
|
|
132
|
+
var hostProfileSections = null;
|
|
133
|
+
var hostHeaderActions = null;
|
|
134
|
+
var hostIsProtocolMessage = null;
|
|
124
135
|
function setUiHost(host) {
|
|
125
136
|
H = host || {};
|
|
137
|
+
hostTabs = Array.isArray(H.hostTabs) ? H.hostTabs : null;
|
|
138
|
+
hostHideApps = Array.isArray(H.hideApps) ? H.hideApps : null;
|
|
139
|
+
hostApps = Array.isArray(H.hostApps) ? H.hostApps : null;
|
|
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;
|
|
126
143
|
hostNeedsWelcome = !!H.hostNeedsWelcome;
|
|
127
144
|
hostPrefersRtcFile = !!H.hostPrefersRtcFile;
|
|
128
145
|
hostHasKeyBackup = !!H.hostHasKeyBackup;
|
|
@@ -1489,6 +1506,100 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
|
|
|
1489
1506
|
return { thread, handle, removeFriends, adoptServicePeers };
|
|
1490
1507
|
}
|
|
1491
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
|
+
|
|
1492
1603
|
// src/chat.jsx
|
|
1493
1604
|
var DK_CMD_HIST_KEY = "beagle.assistant.history";
|
|
1494
1605
|
var DK_CMD_HIST_MAX = 50;
|
|
@@ -1550,8 +1661,9 @@ function RequestsBlock({ T, requests, onAct }) {
|
|
|
1550
1661
|
paddingLeft: 8
|
|
1551
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))))));
|
|
1552
1663
|
}
|
|
1553
|
-
function PeerRow({ peer, T, active, onClick, muted }) {
|
|
1664
|
+
function PeerRow({ peer, T, active, onClick, muted, isProtocol }) {
|
|
1554
1665
|
const name = peer.alias || peer.userId;
|
|
1666
|
+
const row = rosterRow(peer, isProtocol || (() => false));
|
|
1555
1667
|
return /* @__PURE__ */ React.createElement("button", { onClick, style: {
|
|
1556
1668
|
display: "flex",
|
|
1557
1669
|
alignItems: "center",
|
|
@@ -1580,12 +1692,13 @@ function PeerRow({ peer, T, active, onClick, muted }) {
|
|
|
1580
1692
|
textOverflow: "ellipsis",
|
|
1581
1693
|
flex: 1,
|
|
1582
1694
|
minWidth: 0
|
|
1583
|
-
} }, 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 })));
|
|
1584
1696
|
}
|
|
1585
|
-
function dkActivityTs(p) {
|
|
1586
|
-
|
|
1697
|
+
function dkActivityTs(p, isProtocol) {
|
|
1698
|
+
const row = rosterRow(p, isProtocol || (() => false));
|
|
1699
|
+
return Math.max(row.activityTs, p.acceptedAt || 0, p.requestedAt || 0);
|
|
1587
1700
|
}
|
|
1588
|
-
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 }) {
|
|
1589
1702
|
const [q, setQ] = React.useState("");
|
|
1590
1703
|
const [addr, setAddr] = React.useState("");
|
|
1591
1704
|
const [addState, setAddState] = React.useState(null);
|
|
@@ -1630,7 +1743,7 @@ function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, pre
|
|
|
1630
1743
|
}).slice().sort((a, b) => {
|
|
1631
1744
|
if (!!a.pinned !== !!b.pinned)
|
|
1632
1745
|
return a.pinned ? -1 : 1;
|
|
1633
|
-
const ta = dkActivityTs(a), tb = dkActivityTs(b);
|
|
1746
|
+
const ta = dkActivityTs(a, isProtocol), tb = dkActivityTs(b, isProtocol);
|
|
1634
1747
|
if (ta !== tb)
|
|
1635
1748
|
return tb - ta;
|
|
1636
1749
|
return String(a.alias || a.userId || "").localeCompare(String(b.alias || b.userId || ""));
|
|
@@ -1669,7 +1782,7 @@ function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, pre
|
|
|
1669
1782
|
fontSize: 11.5,
|
|
1670
1783
|
lineHeight: 1.35,
|
|
1671
1784
|
color: addState.kind === "err" ? "var(--bad, #e5484d)" : addState.kind === "ok" ? "var(--good, #46a758)" : "var(--faint)"
|
|
1672
|
-
} }, 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"))));
|
|
1673
1786
|
}
|
|
1674
1787
|
var inputStyle = {
|
|
1675
1788
|
flex: 1,
|
|
@@ -2669,7 +2782,7 @@ function Msg({ m, peer, T, lang, onTheater, onDelete, onCancel, onRetry, onRetry
|
|
|
2669
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)" })))
|
|
2670
2783
|
);
|
|
2671
2784
|
}
|
|
2672
|
-
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 }) {
|
|
2673
2786
|
const scrollRef = React.useRef(null);
|
|
2674
2787
|
const prependRef = React.useRef(null);
|
|
2675
2788
|
const [earlierBusy, setEarlierBusy] = React.useState(false);
|
|
@@ -3032,7 +3145,7 @@ function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, on
|
|
|
3032
3145
|
// The internal id is not an address anyone can use and not a name
|
|
3033
3146
|
// anyone chose; saying what this row IS is more use than showing it.
|
|
3034
3147
|
/* @__PURE__ */ React.createElement("div", { style: { marginTop: 2, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, T && T.assistantSub || "Local \xB7 APP")
|
|
3035
|
-
) : 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(
|
|
3148
|
+
) : 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(
|
|
3036
3149
|
Mono,
|
|
3037
3150
|
{
|
|
3038
3151
|
size: 11.5,
|
|
@@ -3067,7 +3180,7 @@ ${peer.address}`
|
|
|
3067
3180
|
} }), phone && onSendRtcFile && !peer.app && /* @__PURE__ */ React.createElement(MenuItem, { icon: "file", label: T && T.sendWebrtcFile || "Send file via WebRTC", onClick: () => {
|
|
3068
3181
|
setMenu(false);
|
|
3069
3182
|
rtcFileRef.current && rtcFileRef.current.click();
|
|
3070
|
-
} }), phone && !peer.app && /* @__PURE__ */ React.createElement(MenuItem, { icon: "network", label: T && T.openNet || "Network view", onClick: () => {
|
|
3183
|
+
} }), phone && !peer.app && onOpenNet && /* @__PURE__ */ React.createElement(MenuItem, { icon: "network", label: T && T.openNet || "Network view", onClick: () => {
|
|
3071
3184
|
setMenu(false);
|
|
3072
3185
|
onOpenNet(peer);
|
|
3073
3186
|
} }), !peer.app && /* @__PURE__ */ React.createElement(MenuItem, { icon: "hash", label: T.alias, onClick: () => {
|
|
@@ -3091,7 +3204,15 @@ ${peer.address}`
|
|
|
3091
3204
|
fontSize: 12.5,
|
|
3092
3205
|
color: "var(--text)"
|
|
3093
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.")),
|
|
3094
|
-
/* @__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(
|
|
3095
3216
|
Msg,
|
|
3096
3217
|
{
|
|
3097
3218
|
key: m.id || i,
|
|
@@ -3262,12 +3383,12 @@ function MenuItem({ icon, label, onClick, danger }) {
|
|
|
3262
3383
|
function ChatEmpty({ T }) {
|
|
3263
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));
|
|
3264
3385
|
}
|
|
3265
|
-
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 }) {
|
|
3266
3387
|
const peer = peers.find((p) => p.id === activeId);
|
|
3267
3388
|
if (phone) {
|
|
3268
|
-
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 });
|
|
3269
3390
|
}
|
|
3270
|
-
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 }));
|
|
3271
3392
|
}
|
|
3272
3393
|
Object.assign(window, { ChatTab });
|
|
3273
3394
|
|
|
@@ -4042,7 +4163,7 @@ function DkAvatarSheet({ T, me, zh, onSet, onClose }) {
|
|
|
4042
4163
|
return /* @__PURE__ */ React.createElement(DkPunkPicker, { T, onPick: pickPunk, onClose: () => setPicking(false) });
|
|
4043
4164
|
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)));
|
|
4044
4165
|
}
|
|
4045
|
-
function ProfileTab({ T, me, onEdit, t, setTweak, onSetAvatar }) {
|
|
4166
|
+
function ProfileTab({ T, me, onEdit, t, setTweak, onSetAvatar, sections, sectionProps }) {
|
|
4046
4167
|
var _a;
|
|
4047
4168
|
const [avatarSheet, setAvatarSheet] = React.useState(false);
|
|
4048
4169
|
const [qr, setQr] = React.useState(null);
|
|
@@ -4074,12 +4195,33 @@ function ProfileTab({ T, me, onEdit, t, setTweak, onSetAvatar }) {
|
|
|
4074
4195
|
},
|
|
4075
4196
|
style: { width: 16, height: 16, accentColor: "var(--accent)" }
|
|
4076
4197
|
}
|
|
4077
|
-
), /* @__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) => {
|
|
4198
|
+
), /* @__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) => {
|
|
4078
4199
|
if (onEdit)
|
|
4079
4200
|
onEdit(name, description);
|
|
4080
4201
|
setEditing(false);
|
|
4081
4202
|
} }));
|
|
4082
4203
|
}
|
|
4204
|
+
var DkHostSection = class extends React.Component {
|
|
4205
|
+
constructor(props) {
|
|
4206
|
+
super(props);
|
|
4207
|
+
this.state = { err: null };
|
|
4208
|
+
}
|
|
4209
|
+
static getDerivedStateFromError(err) {
|
|
4210
|
+
return { err };
|
|
4211
|
+
}
|
|
4212
|
+
render() {
|
|
4213
|
+
var _a;
|
|
4214
|
+
const { sec, props, lang } = this.props;
|
|
4215
|
+
if (this.state.err) {
|
|
4216
|
+
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.");
|
|
4217
|
+
}
|
|
4218
|
+
try {
|
|
4219
|
+
return (_a = sec.render(props)) != null ? _a : null;
|
|
4220
|
+
} catch (e) {
|
|
4221
|
+
return null;
|
|
4222
|
+
}
|
|
4223
|
+
}
|
|
4224
|
+
};
|
|
4083
4225
|
Object.assign(window, { DkShareSheet, ProfileTab, DkPunkAvatar, DkEnsAvatar, DkPublicProfile });
|
|
4084
4226
|
|
|
4085
4227
|
// src/ui.jsx
|
|
@@ -4296,6 +4438,84 @@ function Section({ label, count, trailing, style }) {
|
|
|
4296
4438
|
}
|
|
4297
4439
|
Object.assign(window, { useIsPhone, DkPhoneStyles, DkIdenticon, DkBot, DkAvatar, DkImgAvatar, StatusDot, Mono, shortKey, RouteTag, Tag, Unread, Btn, Section });
|
|
4298
4440
|
|
|
4441
|
+
// src/host-tabs.js
|
|
4442
|
+
var BUILTIN_TABS = ["chat", "people", "network", "apps", "profile"];
|
|
4443
|
+
var warned = /* @__PURE__ */ new Set();
|
|
4444
|
+
function warnOnce(what) {
|
|
4445
|
+
if (warned.has(what))
|
|
4446
|
+
return;
|
|
4447
|
+
warned.add(what);
|
|
4448
|
+
try {
|
|
4449
|
+
console.warn(`[beagle-ui] ignoring unknown host tab or app: ${what}`);
|
|
4450
|
+
} catch (e) {
|
|
4451
|
+
}
|
|
4452
|
+
}
|
|
4453
|
+
function pickText(value, lang, fallback = "") {
|
|
4454
|
+
if (typeof value === "string")
|
|
4455
|
+
return value;
|
|
4456
|
+
if (value && typeof value === "object")
|
|
4457
|
+
return value[lang] || value.en || value.zh || fallback;
|
|
4458
|
+
return fallback;
|
|
4459
|
+
}
|
|
4460
|
+
function resolveTabs(hostTabs2) {
|
|
4461
|
+
if (!Array.isArray(hostTabs2) || !hostTabs2.length) {
|
|
4462
|
+
return BUILTIN_TABS.map((id) => ({ id, kind: "builtin" }));
|
|
4463
|
+
}
|
|
4464
|
+
const out = [];
|
|
4465
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4466
|
+
for (const entry of hostTabs2) {
|
|
4467
|
+
if (typeof entry === "string") {
|
|
4468
|
+
if (!BUILTIN_TABS.includes(entry)) {
|
|
4469
|
+
warnOnce(entry);
|
|
4470
|
+
continue;
|
|
4471
|
+
}
|
|
4472
|
+
if (seen.has(entry))
|
|
4473
|
+
continue;
|
|
4474
|
+
seen.add(entry);
|
|
4475
|
+
out.push({ id: entry, kind: "builtin" });
|
|
4476
|
+
continue;
|
|
4477
|
+
}
|
|
4478
|
+
if (entry && typeof entry === "object" && typeof entry.id === "string" && entry.id) {
|
|
4479
|
+
if (typeof entry.render !== "function") {
|
|
4480
|
+
warnOnce(`${entry.id} (no render function)`);
|
|
4481
|
+
continue;
|
|
4482
|
+
}
|
|
4483
|
+
if (BUILTIN_TABS.includes(entry.id)) {
|
|
4484
|
+
warnOnce(`${entry.id} (a built-in id cannot be redefined)`);
|
|
4485
|
+
continue;
|
|
4486
|
+
}
|
|
4487
|
+
if (seen.has(entry.id))
|
|
4488
|
+
continue;
|
|
4489
|
+
seen.add(entry.id);
|
|
4490
|
+
out.push({
|
|
4491
|
+
id: entry.id,
|
|
4492
|
+
kind: "host",
|
|
4493
|
+
label: entry.label,
|
|
4494
|
+
icon: entry.icon,
|
|
4495
|
+
render: entry.render,
|
|
4496
|
+
badge: typeof entry.badge === "function" ? entry.badge : void 0
|
|
4497
|
+
});
|
|
4498
|
+
continue;
|
|
4499
|
+
}
|
|
4500
|
+
warnOnce(String(entry && entry.id ? entry.id : entry));
|
|
4501
|
+
}
|
|
4502
|
+
return out.length ? out : BUILTIN_TABS.map((id) => ({ id, kind: "builtin" }));
|
|
4503
|
+
}
|
|
4504
|
+
function hasTab(tabs, id) {
|
|
4505
|
+
return (tabs != null ? tabs : []).some((t) => t.id === id);
|
|
4506
|
+
}
|
|
4507
|
+
function resolveApps(builtins, hideApps, hostApps2) {
|
|
4508
|
+
const hidden = new Set(Array.isArray(hideApps) ? hideApps : []);
|
|
4509
|
+
const host = (Array.isArray(hostApps2) ? hostApps2 : []).filter((a) => {
|
|
4510
|
+
if (a && typeof a.id === "string" && a.id && typeof a.open === "function")
|
|
4511
|
+
return true;
|
|
4512
|
+
warnOnce(`app ${a && a.id ? a.id : String(a)}`);
|
|
4513
|
+
return false;
|
|
4514
|
+
}).map((a) => ({ ...a, host: true }));
|
|
4515
|
+
const kept = (builtins != null ? builtins : []).filter((a) => !hidden.has(a.id));
|
|
4516
|
+
return host.concat(kept);
|
|
4517
|
+
}
|
|
4518
|
+
|
|
4299
4519
|
// src/apps.jsx
|
|
4300
4520
|
var APPS = [
|
|
4301
4521
|
{
|
|
@@ -4336,11 +4556,24 @@ function dkB64Url(obj) {
|
|
|
4336
4556
|
const b64 = btoa(unescape(encodeURIComponent(json)));
|
|
4337
4557
|
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
4338
4558
|
}
|
|
4339
|
-
function AppsTab({ T, t }) {
|
|
4559
|
+
function AppsTab({ T, t, me, peers, phone, setTab }) {
|
|
4340
4560
|
const zh = t && t.lang === "zh";
|
|
4561
|
+
const apps = React.useMemo(
|
|
4562
|
+
() => resolveApps(APPS, hostHideApps, hostApps),
|
|
4563
|
+
[]
|
|
4564
|
+
);
|
|
4341
4565
|
const [busy, setBusy] = React.useState(null);
|
|
4342
4566
|
const [err, setErr] = React.useState(null);
|
|
4343
4567
|
const open = async (app) => {
|
|
4568
|
+
if (app.host) {
|
|
4569
|
+
setErr(null);
|
|
4570
|
+
try {
|
|
4571
|
+
app.open({ T, t, me, peers, phone, setTab });
|
|
4572
|
+
} catch (e) {
|
|
4573
|
+
setErr(String(e && e.message || e));
|
|
4574
|
+
}
|
|
4575
|
+
return;
|
|
4576
|
+
}
|
|
4344
4577
|
setBusy(app.id);
|
|
4345
4578
|
setErr(null);
|
|
4346
4579
|
const win = window.open("", "_blank");
|
|
@@ -4362,7 +4595,7 @@ function AppsTab({ T, t }) {
|
|
|
4362
4595
|
setBusy(null);
|
|
4363
4596
|
}
|
|
4364
4597
|
};
|
|
4365
|
-
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),
|
|
4598
|
+
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: {
|
|
4366
4599
|
display: "flex",
|
|
4367
4600
|
alignItems: "center",
|
|
4368
4601
|
gap: 14,
|
|
@@ -4379,7 +4612,7 @@ function AppsTab({ T, t }) {
|
|
|
4379
4612
|
display: "flex",
|
|
4380
4613
|
alignItems: "center",
|
|
4381
4614
|
justifyContent: "center"
|
|
4382
|
-
} }, /* @__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 } },
|
|
4615
|
+
} }, /* @__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")))));
|
|
4383
4616
|
}
|
|
4384
4617
|
|
|
4385
4618
|
// src/rtc-signaling.jsx
|
|
@@ -5799,7 +6032,7 @@ function dkFlashStop() {
|
|
|
5799
6032
|
function dkLooking() {
|
|
5800
6033
|
return document.visibilityState === "visible" && document.hasFocus();
|
|
5801
6034
|
}
|
|
5802
|
-
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 }) {
|
|
5803
6036
|
const prev = React.useRef(null);
|
|
5804
6037
|
const mutedRef = React.useRef(/* @__PURE__ */ new Set());
|
|
5805
6038
|
mutedRef.current = new Set(muted || []);
|
|
@@ -5829,7 +6062,8 @@ function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true,
|
|
|
5829
6062
|
const byId = /* @__PURE__ */ new Map();
|
|
5830
6063
|
for (const p of peers || []) {
|
|
5831
6064
|
const dir = p.lastDir || (String(p.lastMsg || "").startsWith("you: ") ? "out" : "in");
|
|
5832
|
-
|
|
6065
|
+
const quiet = typeof isProtocol === "function" && lastIsProtocol(p, isProtocol);
|
|
6066
|
+
byId.set(p.id, { ts: p.lastTs || 0, dir, quiet });
|
|
5833
6067
|
}
|
|
5834
6068
|
const reqIds = new Set((requests || []).map((r) => r.id));
|
|
5835
6069
|
const was = prev.current;
|
|
@@ -5840,6 +6074,8 @@ function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true,
|
|
|
5840
6074
|
for (const [id, cur] of byId) {
|
|
5841
6075
|
if (cur.dir !== "in" || !cur.ts)
|
|
5842
6076
|
continue;
|
|
6077
|
+
if (cur.quiet)
|
|
6078
|
+
continue;
|
|
5843
6079
|
const old = was.byId.get(id);
|
|
5844
6080
|
if (old ? cur.ts <= old.ts : Date.now() - cur.ts > 3e4)
|
|
5845
6081
|
continue;
|
|
@@ -6384,7 +6620,7 @@ var STR = {
|
|
|
6384
6620
|
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"
|
|
6385
6621
|
}
|
|
6386
6622
|
};
|
|
6387
|
-
function RailBtn({ icon, label, active, soon, onClick }) {
|
|
6623
|
+
function RailBtn({ icon, label, active, soon, onClick, svgPath }) {
|
|
6388
6624
|
return /* @__PURE__ */ React.createElement("button", { onClick, title: label, style: {
|
|
6389
6625
|
width: 52,
|
|
6390
6626
|
height: 52,
|
|
@@ -6399,9 +6635,9 @@ function RailBtn({ icon, label, active, soon, onClick }) {
|
|
|
6399
6635
|
alignItems: "center",
|
|
6400
6636
|
justifyContent: "center",
|
|
6401
6637
|
gap: 3
|
|
6402
|
-
} }, /* @__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)" } }));
|
|
6638
|
+
} }, /* @__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)" } }));
|
|
6403
6639
|
}
|
|
6404
|
-
function TabBarBtn({ icon, label, active, badge, onClick }) {
|
|
6640
|
+
function TabBarBtn({ icon, label, active, badge, onClick, svgPath }) {
|
|
6405
6641
|
return /* @__PURE__ */ React.createElement("button", { onClick, style: {
|
|
6406
6642
|
flex: 1,
|
|
6407
6643
|
minWidth: 0,
|
|
@@ -6416,9 +6652,36 @@ function TabBarBtn({ icon, label, active, badge, onClick }) {
|
|
|
6416
6652
|
justifyContent: "center",
|
|
6417
6653
|
gap: 3,
|
|
6418
6654
|
fontFamily: "var(--mono)"
|
|
6419
|
-
} }, /* @__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);
|
|
6655
|
+
} }, /* @__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);
|
|
6420
6656
|
}
|
|
6421
6657
|
var DK_PEOPLE_TABS = ["here", "recommended", "registered"];
|
|
6658
|
+
var HostTabPane = class extends React.Component {
|
|
6659
|
+
constructor(props) {
|
|
6660
|
+
super(props);
|
|
6661
|
+
this.state = { err: null };
|
|
6662
|
+
}
|
|
6663
|
+
static getDerivedStateFromError(err) {
|
|
6664
|
+
return { err };
|
|
6665
|
+
}
|
|
6666
|
+
componentDidCatch(err) {
|
|
6667
|
+
try {
|
|
6668
|
+
console.error("[beagle-ui] host tab failed", err);
|
|
6669
|
+
} catch (e) {
|
|
6670
|
+
}
|
|
6671
|
+
}
|
|
6672
|
+
render() {
|
|
6673
|
+
var _a;
|
|
6674
|
+
const { tab, props, lang } = this.props;
|
|
6675
|
+
if (this.state.err) {
|
|
6676
|
+
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.`));
|
|
6677
|
+
}
|
|
6678
|
+
try {
|
|
6679
|
+
return (_a = tab.render(props)) != null ? _a : null;
|
|
6680
|
+
} catch (e) {
|
|
6681
|
+
return null;
|
|
6682
|
+
}
|
|
6683
|
+
}
|
|
6684
|
+
};
|
|
6422
6685
|
function Brand({ T, children, style }) {
|
|
6423
6686
|
if (!hostBrandHref)
|
|
6424
6687
|
return /* @__PURE__ */ React.createElement("span", { style }, children);
|
|
@@ -6535,7 +6798,17 @@ function DkUpdateModal({ T, info, onClose }) {
|
|
|
6535
6798
|
}
|
|
6536
6799
|
function DkApp() {
|
|
6537
6800
|
const [t, setTweak] = useTweaks({ ...DK_DEFAULTS, lang: dkDetectLang() });
|
|
6538
|
-
const
|
|
6801
|
+
const tabs = React.useMemo(() => resolveTabs(hostTabs), []);
|
|
6802
|
+
const showPeople = hasTab(tabs, "people");
|
|
6803
|
+
const showNetwork = hasTab(tabs, "network");
|
|
6804
|
+
const showProfile = hasTab(tabs, "profile");
|
|
6805
|
+
const firstTab = tabs[0] ? tabs[0].id === "people" ? "here" : tabs[0].id : "chat";
|
|
6806
|
+
const [tab, setTab] = React.useState(() => {
|
|
6807
|
+
const want = t.startTab || "chat";
|
|
6808
|
+
if (want === "chat" && hasTab(tabs, "chat"))
|
|
6809
|
+
return "chat";
|
|
6810
|
+
return hasTab(tabs, want) || DK_PEOPLE_TABS.includes(want) && showPeople ? want : firstTab;
|
|
6811
|
+
});
|
|
6539
6812
|
const [activeId, setActiveId] = React.useState(null);
|
|
6540
6813
|
const data = useDaemonData();
|
|
6541
6814
|
const me = data.me;
|
|
@@ -6570,11 +6843,13 @@ function DkApp() {
|
|
|
6570
6843
|
});
|
|
6571
6844
|
}, []);
|
|
6572
6845
|
React.useEffect(() => {
|
|
6846
|
+
if (!showNetwork)
|
|
6847
|
+
return void 0;
|
|
6573
6848
|
refreshBackend();
|
|
6574
6849
|
const ms = backend && backend.state === "releasing" ? 1500 : 8e3;
|
|
6575
6850
|
const t2 = setInterval(refreshBackend, ms);
|
|
6576
6851
|
return () => clearInterval(t2);
|
|
6577
|
-
}, [refreshBackend, backend && backend.state]);
|
|
6852
|
+
}, [refreshBackend, backend && backend.state, showNetwork]);
|
|
6578
6853
|
const armLan = React.useCallback(async () => {
|
|
6579
6854
|
await dkPost("/api/lan-arm", {});
|
|
6580
6855
|
refreshBackend();
|
|
@@ -6648,6 +6923,7 @@ function DkApp() {
|
|
|
6648
6923
|
const rowPad = t.density === "comfortable" ? "11px 12px" : "7px 10px";
|
|
6649
6924
|
const mutedIds = Array.isArray(t.alertMuted) ? t.alertMuted : [];
|
|
6650
6925
|
const onToggleMute = (id) => setTweak("alertMuted", mutedIds.includes(id) ? mutedIds.filter((x) => x !== id) : mutedIds.concat(id));
|
|
6926
|
+
const isProtocol = React.useMemo(() => protocolTest(hostIsProtocolMessage), []);
|
|
6651
6927
|
useIncomingAlerts({
|
|
6652
6928
|
T,
|
|
6653
6929
|
peers,
|
|
@@ -6656,7 +6932,8 @@ function DkApp() {
|
|
|
6656
6932
|
activeId,
|
|
6657
6933
|
sound: t.alertSound !== false,
|
|
6658
6934
|
flash: t.alertFlash !== false,
|
|
6659
|
-
muted: mutedIds
|
|
6935
|
+
muted: mutedIds,
|
|
6936
|
+
isProtocol
|
|
6660
6937
|
});
|
|
6661
6938
|
const assistant = useAssistant({
|
|
6662
6939
|
lang: t.lang,
|
|
@@ -6690,6 +6967,54 @@ function DkApp() {
|
|
|
6690
6967
|
});
|
|
6691
6968
|
const phone = useIsPhone();
|
|
6692
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
|
+
});
|
|
6693
7018
|
const shareBtn = /* @__PURE__ */ React.createElement(
|
|
6694
7019
|
"button",
|
|
6695
7020
|
{
|
|
@@ -6843,36 +7168,68 @@ function DkApp() {
|
|
|
6843
7168
|
setTab("chat");
|
|
6844
7169
|
openConversation(id);
|
|
6845
7170
|
};
|
|
6846
|
-
const onOpenNet = () => setTab("network");
|
|
6847
|
-
const
|
|
6848
|
-
{ id: "chat", icon: "message", label: T.chat },
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
7171
|
+
const onOpenNet = showNetwork ? () => setTab("network") : null;
|
|
7172
|
+
const RAIL_FOR = {
|
|
7173
|
+
chat: [{ id: "chat", icon: "message", label: T.chat }],
|
|
7174
|
+
people: [
|
|
7175
|
+
{ id: "here", icon: "users", label: T.here },
|
|
7176
|
+
{ id: "recommended", icon: "sparkles", label: T.recommended },
|
|
7177
|
+
{ id: "registered", icon: "at", label: T.registered }
|
|
7178
|
+
],
|
|
7179
|
+
network: [{ id: "network", icon: "network", label: T.network }],
|
|
7180
|
+
apps: [{ id: "apps", icon: "grid", label: T.apps }],
|
|
7181
|
+
profile: []
|
|
7182
|
+
};
|
|
7183
|
+
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) }]);
|
|
6855
7184
|
const peopleTab = DK_PEOPLE_TABS.includes(tab);
|
|
6856
7185
|
const lastPeople = React.useRef("here");
|
|
6857
7186
|
if (peopleTab)
|
|
6858
7187
|
lastPeople.current = tab;
|
|
6859
|
-
const unreadTotal = (peers
|
|
6860
|
-
const
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
7188
|
+
const unreadTotal = rosterUnreadTotal(peers, isProtocol) + (requests || []).length;
|
|
7189
|
+
const NAV_FOR = {
|
|
7190
|
+
chat: { icon: "message", label: T.chat, active: tab === "chat", badge: unreadTotal, go: () => setTab("chat") },
|
|
7191
|
+
people: { icon: "users", label: T.people, active: peopleTab, go: () => setTab(lastPeople.current) },
|
|
7192
|
+
network: { icon: "network", label: T.network, active: tab === "network", go: () => setTab("network") },
|
|
7193
|
+
apps: { icon: "grid", label: T.apps, active: tab === "apps", go: () => setTab("apps") },
|
|
7194
|
+
profile: { icon: "userRound", label: T.profile, active: tab === "profile", go: () => setTab("profile") }
|
|
7195
|
+
};
|
|
7196
|
+
const phoneNav = tabs.map((x) => x.kind === "builtin" ? { id: x.id, ...NAV_FOR[x.id] } : {
|
|
7197
|
+
id: x.id,
|
|
7198
|
+
icon: typeof x.icon === "string" ? x.icon : void 0,
|
|
7199
|
+
svgPath: x.icon && typeof x.icon === "object" ? x.icon.svgPath : void 0,
|
|
7200
|
+
label: pickText(x.label, t.lang, x.id),
|
|
7201
|
+
active: tab === x.id,
|
|
7202
|
+
// A host badge is called on every render, like the unread count beside
|
|
7203
|
+
// it; a throwing one must not take the tab bar down with it.
|
|
7204
|
+
badge: (() => {
|
|
7205
|
+
try {
|
|
7206
|
+
return x.badge ? x.badge() : 0;
|
|
7207
|
+
} catch (e) {
|
|
7208
|
+
return 0;
|
|
7209
|
+
}
|
|
7210
|
+
})(),
|
|
7211
|
+
go: () => setTab(x.id)
|
|
7212
|
+
});
|
|
6867
7213
|
const inConversation = phone && tab === "chat" && !!activeId && (peers || []).some((p) => p.id === activeId);
|
|
6868
|
-
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(
|
|
7214
|
+
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(
|
|
7215
|
+
RailBtn,
|
|
7216
|
+
{
|
|
7217
|
+
key: n.id,
|
|
7218
|
+
icon: typeof n.icon === "string" ? n.icon : void 0,
|
|
7219
|
+
svgPath: n.icon && typeof n.icon === "object" ? n.icon.svgPath : void 0,
|
|
7220
|
+
label: n.label,
|
|
7221
|
+
active: tab === n.id,
|
|
7222
|
+
soon: n.soon,
|
|
7223
|
+
onClick: () => setTab(n.id)
|
|
7224
|
+
}
|
|
7225
|
+
)), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), showProfile && /* @__PURE__ */ React.createElement("button", { onClick: () => setTab("profile"), title: T.profile, style: {
|
|
6869
7226
|
padding: 4,
|
|
6870
7227
|
borderRadius: 12,
|
|
6871
7228
|
cursor: "pointer",
|
|
6872
7229
|
display: "flex",
|
|
6873
7230
|
border: "1px solid " + (tab === "profile" ? "var(--line)" : "transparent"),
|
|
6874
7231
|
background: tab === "profile" ? "var(--panel-2)" : "transparent"
|
|
6875
|
-
} }, /* @__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: {
|
|
6876
7233
|
display: "inline-flex",
|
|
6877
7234
|
alignItems: "center",
|
|
6878
7235
|
gap: 6,
|
|
@@ -6886,10 +7243,22 @@ function DkApp() {
|
|
|
6886
7243
|
fontWeight: 600,
|
|
6887
7244
|
background: tab === n.id ? "var(--accent)" : "transparent",
|
|
6888
7245
|
color: tab === n.id ? "#fff" : "var(--dim)"
|
|
6889
|
-
} }, /* @__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 }),
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
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(
|
|
7247
|
+
ProfileTab,
|
|
7248
|
+
{
|
|
7249
|
+
T,
|
|
7250
|
+
me,
|
|
7251
|
+
onEdit,
|
|
7252
|
+
t,
|
|
7253
|
+
setTweak,
|
|
7254
|
+
onSetAvatar: (p) => dkApi.setProfileFull(p).then((r) => {
|
|
7255
|
+
data.refresh();
|
|
7256
|
+
return r;
|
|
7257
|
+
}),
|
|
7258
|
+
sections: hostProfileSections,
|
|
7259
|
+
sectionProps: { T, t, me, peers, phone, setTab }
|
|
7260
|
+
}
|
|
7261
|
+
)), 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(
|
|
6893
7262
|
DkLockedOut,
|
|
6894
7263
|
{
|
|
6895
7264
|
compact: true,
|
package/package.json
CHANGED