@decentnetwork/beagle-ui 0.2.35 → 0.2.37
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 +197 -20
- 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.37";
|
|
2
2
|
// src/icons.jsx
|
|
3
3
|
var ICON_PATHS = {
|
|
4
4
|
// ---- tab bar (the four must feel like one set) ----
|
|
@@ -276,7 +276,7 @@ function collapseProtocol(thread, isProto, lang) {
|
|
|
276
276
|
};
|
|
277
277
|
(thread || []).forEach((m, i) => {
|
|
278
278
|
const hit = m && !m.day ? isProto({ text: m.text, dir: m && m.from === "me" ? "out" : "in", ts: m && m.ts }) : false;
|
|
279
|
-
if (!hit || m && m.file) {
|
|
279
|
+
if (!hit || m && (m.file || m.kind === "friend-request")) {
|
|
280
280
|
flush();
|
|
281
281
|
out.push(m);
|
|
282
282
|
return;
|
|
@@ -448,6 +448,10 @@ var dkApi = {
|
|
|
448
448
|
cancelSend: (userid, ids) => dkPost("/api/file-cancel", { userid, ids }),
|
|
449
449
|
retrySend: (userid, ids) => dkPost("/api/file-retry", { userid, ids }),
|
|
450
450
|
setProfile: (name, description) => dkPost("/api/set-profile", { name, description }),
|
|
451
|
+
// Probe a failed exit once, now (or every failed exit with all=true). Exits
|
|
452
|
+
// that failed three times in a row are never probed again on their own.
|
|
453
|
+
reconnectExit: (ip) => dkPost("/api/exits/reconnect", { ip }),
|
|
454
|
+
reconnectAllExits: () => dkPost("/api/exits/reconnect", { all: true }),
|
|
451
455
|
// Used by the browser's welcome flow, which also picks an avatar and marks
|
|
452
456
|
// first run done. Harmless where there is no welcome flow: nothing calls it.
|
|
453
457
|
setProfileFull: (p) => dkPost("/api/set-profile", p),
|
|
@@ -535,7 +539,10 @@ function dkThreadMsg(m) {
|
|
|
535
539
|
// Brief 30: a validated component form, already whitelisted host-side.
|
|
536
540
|
// Passed through untouched — the renderer treats every string as text,
|
|
537
541
|
// never markup.
|
|
538
|
-
components: m.components
|
|
542
|
+
components: m.components,
|
|
543
|
+
// 'friend-request': the accepted request's greeting, merged in by the
|
|
544
|
+
// host (beagle's friend-greetings.ts); chat.jsx draws its own bubble.
|
|
545
|
+
kind: m.kind
|
|
539
546
|
};
|
|
540
547
|
}
|
|
541
548
|
var DK_TAIL_MIN = 30;
|
|
@@ -580,22 +587,27 @@ function useProtocolTails(peers, isProtocol, enabled) {
|
|
|
580
587
|
}, [peers, isProtocol, enabled]);
|
|
581
588
|
return fixes;
|
|
582
589
|
}
|
|
583
|
-
function useDaemonData() {
|
|
590
|
+
function useDaemonData(opts) {
|
|
591
|
+
const wantExits = !!(opts && opts.exits);
|
|
592
|
+
const wantExitsRef = React.useRef(wantExits);
|
|
593
|
+
wantExitsRef.current = wantExits;
|
|
584
594
|
const [snap, setSnap] = React.useState({
|
|
585
595
|
me: DK_ME_FALLBACK,
|
|
586
596
|
peers: [],
|
|
587
597
|
requests: [],
|
|
588
598
|
exits: [],
|
|
589
599
|
activeExit: null,
|
|
600
|
+
exitFeature: null,
|
|
601
|
+
routeFallback: null,
|
|
590
602
|
loaded: false,
|
|
591
603
|
locked: false
|
|
592
604
|
});
|
|
593
605
|
const [threads, setThreads] = React.useState({});
|
|
594
606
|
const refreshRef = React.useRef(null);
|
|
595
607
|
const refresh = React.useCallback(async () => {
|
|
596
|
-
var _a;
|
|
608
|
+
var _a, _b, _c;
|
|
597
609
|
try {
|
|
598
|
-
const d = await dkGet("/api/desktop");
|
|
610
|
+
const d = await dkGet(wantExitsRef.current ? "/api/desktop?exits=1" : "/api/desktop");
|
|
599
611
|
if (d && d.locked) {
|
|
600
612
|
setSnap((s) => ({ ...s, locked: true, loaded: true }));
|
|
601
613
|
return;
|
|
@@ -613,6 +625,8 @@ function useDaemonData() {
|
|
|
613
625
|
requests: d.requests || [],
|
|
614
626
|
exits: d.exits || [],
|
|
615
627
|
activeExit: (_a = d.activeExit) != null ? _a : null,
|
|
628
|
+
exitFeature: (_b = d.exitFeature) != null ? _b : null,
|
|
629
|
+
routeFallback: (_c = d.routeFallback) != null ? _c : null,
|
|
616
630
|
loaded: true
|
|
617
631
|
});
|
|
618
632
|
} catch (e) {
|
|
@@ -624,6 +638,10 @@ function useDaemonData() {
|
|
|
624
638
|
const t = setInterval(refresh, 2500);
|
|
625
639
|
return () => clearInterval(t);
|
|
626
640
|
}, [refresh]);
|
|
641
|
+
React.useEffect(() => {
|
|
642
|
+
if (wantExits)
|
|
643
|
+
refresh();
|
|
644
|
+
}, [wantExits, refresh]);
|
|
627
645
|
const inFlight = React.useRef(/* @__PURE__ */ new Set());
|
|
628
646
|
const cache = React.useRef({});
|
|
629
647
|
const [earlier, setEarlier] = React.useState({});
|
|
@@ -632,15 +650,23 @@ function useDaemonData() {
|
|
|
632
650
|
if (!c)
|
|
633
651
|
return;
|
|
634
652
|
const arr = [...c.byId.values()].sort((a, b) => a.ts - b.ts);
|
|
635
|
-
const
|
|
636
|
-
|
|
653
|
+
const withDay = [];
|
|
654
|
+
let lastDay = null;
|
|
655
|
+
for (const m of arr) {
|
|
656
|
+
const day = dkDayLabel(m.ts);
|
|
657
|
+
if (day !== lastDay) {
|
|
658
|
+
withDay.push({ day });
|
|
659
|
+
lastDay = day;
|
|
660
|
+
}
|
|
661
|
+
withDay.push(dkThreadMsg(m));
|
|
662
|
+
}
|
|
637
663
|
setThreads((t) => Object.assign({}, t, { [peerId]: withDay }));
|
|
638
664
|
setEarlier((e) => e[peerId] === c.more ? e : Object.assign({}, e, { [peerId]: c.more }));
|
|
639
665
|
}, []);
|
|
640
|
-
const loadThread = React.useCallback(async (peerId,
|
|
666
|
+
const loadThread = React.useCallback(async (peerId, opts2) => {
|
|
641
667
|
if (!peerId)
|
|
642
668
|
return;
|
|
643
|
-
if (
|
|
669
|
+
if (opts2 && opts2.reset)
|
|
644
670
|
delete cache.current[peerId];
|
|
645
671
|
if (inFlight.current.has(peerId))
|
|
646
672
|
return;
|
|
@@ -1745,6 +1771,27 @@ function requestAvatarSource(r, skipUrl = false) {
|
|
|
1745
1771
|
return { kind: "ens" };
|
|
1746
1772
|
}
|
|
1747
1773
|
|
|
1774
|
+
// src/request-greeting.js
|
|
1775
|
+
var REQUEST_TRANSPORTS = ["lan", "carrier", "direct", "relay", "express", "webrtc", "online", "offline", "tcp", "udp", "p2p"];
|
|
1776
|
+
function unquote(s) {
|
|
1777
|
+
const m = /^"([\s\S]*)"$/.exec(s);
|
|
1778
|
+
return m ? m[1] : s;
|
|
1779
|
+
}
|
|
1780
|
+
function requestGreeting(r) {
|
|
1781
|
+
const hello = r && typeof r.hello === "string" ? r.hello.trim() : "";
|
|
1782
|
+
const via = r && typeof r.via === "string" ? r.via.trim() : "";
|
|
1783
|
+
const isTransport = !!via && REQUEST_TRANSPORTS.includes(via.toLowerCase());
|
|
1784
|
+
const isName = !!via && !!r && typeof r.name === "string" && r.name.trim() === via;
|
|
1785
|
+
return {
|
|
1786
|
+
greeting: hello || (via && !isTransport && !isName ? unquote(via).trim() : ""),
|
|
1787
|
+
transport: isTransport ? via : ""
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
var GREETING_KIND = "friend-request";
|
|
1791
|
+
function isGreetingItem(m) {
|
|
1792
|
+
return !!m && m.kind === GREETING_KIND;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1748
1795
|
// src/chat.jsx
|
|
1749
1796
|
var DK_CMD_HIST_KEY = "beagle.assistant.history";
|
|
1750
1797
|
var DK_CMD_HIST_MAX = 50;
|
|
@@ -1797,7 +1844,7 @@ function RequestsBlock({ T, requests, onAct }) {
|
|
|
1797
1844
|
overflow: "hidden",
|
|
1798
1845
|
textOverflow: "ellipsis",
|
|
1799
1846
|
whiteSpace: "nowrap"
|
|
1800
|
-
} }, r.name) : /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", fontStyle: "italic" } }, T.noName || "no name given"), /* @__PURE__ */ React.createElement(Mono, { size: 11, copy: r.carrier, title: r.carrier }, shortKey(r.carrier, 8, 6)), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 10.5, color: "var(--faint)", marginTop: 2 } }, "via "
|
|
1847
|
+
} }, r.name) : /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", fontStyle: "italic" } }, T.noName || "no name given"), /* @__PURE__ */ React.createElement(Mono, { size: 11, copy: r.carrier, title: r.carrier }, shortKey(r.carrier, 8, 6)), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 10.5, color: "var(--faint)", marginTop: 2 } }, requestGreeting(r).transport ? "via " + requestGreeting(r).transport + " \xB7 " : "", r.time))), /* @__PURE__ */ React.createElement(DkRequestGreeting, { T, text: requestGreeting(r).greeting }), r.descr && /* @__PURE__ */ React.createElement("div", { style: {
|
|
1801
1848
|
fontFamily: "var(--ui)",
|
|
1802
1849
|
fontSize: 12,
|
|
1803
1850
|
color: "var(--dim, var(--faint))",
|
|
@@ -1806,6 +1853,76 @@ function RequestsBlock({ T, requests, onAct }) {
|
|
|
1806
1853
|
paddingLeft: 8
|
|
1807
1854
|
} }, 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))))));
|
|
1808
1855
|
}
|
|
1856
|
+
function DkRequestGreeting({ T, text }) {
|
|
1857
|
+
const [open, setOpen] = React.useState(false);
|
|
1858
|
+
const [clipped, setClipped] = React.useState(false);
|
|
1859
|
+
const ref = React.useRef(null);
|
|
1860
|
+
React.useLayoutEffect(() => {
|
|
1861
|
+
const el = ref.current;
|
|
1862
|
+
if (el && !open)
|
|
1863
|
+
setClipped(el.scrollHeight > el.clientHeight + 1);
|
|
1864
|
+
}, [text, open]);
|
|
1865
|
+
if (!text)
|
|
1866
|
+
return null;
|
|
1867
|
+
const toggle = () => {
|
|
1868
|
+
try {
|
|
1869
|
+
const sel = window.getSelection && window.getSelection();
|
|
1870
|
+
if (sel && String(sel).length)
|
|
1871
|
+
return;
|
|
1872
|
+
} catch (e) {
|
|
1873
|
+
}
|
|
1874
|
+
if (clipped || open)
|
|
1875
|
+
setOpen((o) => !o);
|
|
1876
|
+
};
|
|
1877
|
+
return /* @__PURE__ */ React.createElement(
|
|
1878
|
+
"div",
|
|
1879
|
+
{
|
|
1880
|
+
onClick: toggle,
|
|
1881
|
+
title: !open && clipped ? T.showAll || "show all" : void 0,
|
|
1882
|
+
style: { cursor: clipped || open ? "pointer" : "text" }
|
|
1883
|
+
},
|
|
1884
|
+
/* @__PURE__ */ React.createElement("div", { style: { background: "var(--bub-them)", border: "1px solid var(--line)", borderRadius: 8, padding: "6px 9px" } }, /* @__PURE__ */ React.createElement("div", { ref, style: {
|
|
1885
|
+
fontFamily: "var(--ui)",
|
|
1886
|
+
fontSize: 12.5,
|
|
1887
|
+
color: "var(--text)",
|
|
1888
|
+
lineHeight: 1.45,
|
|
1889
|
+
whiteSpace: "pre-wrap",
|
|
1890
|
+
overflowWrap: "anywhere",
|
|
1891
|
+
userSelect: "text",
|
|
1892
|
+
WebkitUserSelect: "text",
|
|
1893
|
+
...open ? { maxHeight: 240, overflowY: "auto" } : { display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden" }
|
|
1894
|
+
} }, text)),
|
|
1895
|
+
(clipped || open) && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11, color: "var(--accent)", marginTop: 3 } }, open ? T.showLess || "show less" : T.showAll || "show all")
|
|
1896
|
+
);
|
|
1897
|
+
}
|
|
1898
|
+
function DkGreetingMsg({ m, peer, T }) {
|
|
1899
|
+
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "flex-start", alignItems: "flex-end", gap: 8, margin: "3px 0 10px" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer, size: 24, radius: 6, dot: false }), /* @__PURE__ */ React.createElement("div", { style: {
|
|
1900
|
+
maxWidth: "75%",
|
|
1901
|
+
padding: "8px 12px",
|
|
1902
|
+
borderRadius: 12,
|
|
1903
|
+
borderBottomLeftRadius: 4,
|
|
1904
|
+
background: "var(--panel-2)",
|
|
1905
|
+
border: "1px dashed var(--accent)",
|
|
1906
|
+
color: "var(--text)"
|
|
1907
|
+
} }, /* @__PURE__ */ React.createElement("div", { style: {
|
|
1908
|
+
display: "flex",
|
|
1909
|
+
alignItems: "center",
|
|
1910
|
+
gap: 6,
|
|
1911
|
+
marginBottom: 4,
|
|
1912
|
+
fontFamily: "var(--mono)",
|
|
1913
|
+
fontSize: 10.5,
|
|
1914
|
+
color: "var(--accent)",
|
|
1915
|
+
fontWeight: 600
|
|
1916
|
+
} }, /* @__PURE__ */ React.createElement(Icon, { name: "userPlus", size: 12, stroke: 2.2, color: "var(--accent)" }), /* @__PURE__ */ React.createElement("span", null, T.friendRequestItem || "Friend request"), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--faint)", fontWeight: 400 } }, "\xB7 ", m.time)), /* @__PURE__ */ React.createElement("div", { style: {
|
|
1917
|
+
fontFamily: "var(--ui)",
|
|
1918
|
+
fontSize: 13.5,
|
|
1919
|
+
lineHeight: 1.5,
|
|
1920
|
+
whiteSpace: "pre-wrap",
|
|
1921
|
+
overflowWrap: "anywhere",
|
|
1922
|
+
userSelect: "text",
|
|
1923
|
+
WebkitUserSelect: "text"
|
|
1924
|
+
} }, m.text)));
|
|
1925
|
+
}
|
|
1809
1926
|
function DkRequestAvatar({ r, size, radius }) {
|
|
1810
1927
|
const [urlBroken, setUrlBroken] = React.useState(false);
|
|
1811
1928
|
const url = requestAvatarUrl(r);
|
|
@@ -2580,7 +2697,12 @@ function DkChatForm({ form, submitted, peer, onSubmit }) {
|
|
|
2580
2697
|
return null;
|
|
2581
2698
|
}), err && /* @__PURE__ */ React.createElement("div", { style: { color: "var(--danger, #ff6b6b)", fontSize: 11.5, fontFamily: "var(--mono)" } }, err));
|
|
2582
2699
|
}
|
|
2583
|
-
function Msg(
|
|
2700
|
+
function Msg(props) {
|
|
2701
|
+
if (isGreetingItem(props.m))
|
|
2702
|
+
return /* @__PURE__ */ React.createElement(DkGreetingMsg, { m: props.m, peer: props.peer, T: props.T });
|
|
2703
|
+
return /* @__PURE__ */ React.createElement(DkMsgBody, { ...props });
|
|
2704
|
+
}
|
|
2705
|
+
function DkMsgBody({ m, peer, T, lang, onTheater, onDelete, onCancel, onRetry, onRetryMsg, onReveal, onOpenFile, onCall, onFormSubmit, onNameCard, onAssistantRemove, onAssistantAdopt, answered, selMode, selected, onToggleSel, busy, isGroup }) {
|
|
2584
2706
|
const mine = m.from === "me";
|
|
2585
2707
|
const nativeMedia = !m.file ? dkNativeMediaFromText(m.text) : null;
|
|
2586
2708
|
const grp = isGroup && m.dir !== "out" && !m.file && !nativeMedia ? dkGroupParse(m.text) : null;
|
|
@@ -6054,15 +6176,40 @@ function PeerTable({ T, peers, onOpenChat }) {
|
|
|
6054
6176
|
borderBottom: i < peers.length - 1 ? "1px solid var(--line)" : "none"
|
|
6055
6177
|
} }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, minWidth: 0 } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: p, size: 26, radius: 6 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: p.alias ? "var(--ui)" : "var(--mono)", fontSize: 13, fontWeight: 600, color: "var(--text)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } }, p.alias || shortKey(p.userId, 8, 5)), p.agent && /* @__PURE__ */ React.createElement(Icon, { name: "bot", size: 12, color: "var(--faint)", stroke: 2 })), /* @__PURE__ */ React.createElement(Mono, { size: 12.5, dim: true, copy: p.ip }, p.ip), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(RouteTag, { peer: p })), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: p.wire === "64" ? "var(--warn)" : "var(--dim)" } }, p.wire === "64" ? "old\xB764" : "new\xB7163"), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: 6 } }, /* @__PURE__ */ React.createElement(Btn, { icon: "message", size: "sm", title: T.openChat, onClick: () => onOpenChat(p.id) }), /* @__PURE__ */ React.createElement(Btn, { icon: "signal", size: "sm", title: T.ping })))));
|
|
6056
6178
|
}
|
|
6057
|
-
function
|
|
6179
|
+
function ReconnectBtn({ T, ip, onReconnect, label, icon = "refresh" }) {
|
|
6180
|
+
const [busy, setBusy] = React.useState(false);
|
|
6181
|
+
return /* @__PURE__ */ React.createElement(
|
|
6182
|
+
Btn,
|
|
6183
|
+
{
|
|
6184
|
+
size: "sm",
|
|
6185
|
+
icon,
|
|
6186
|
+
disabled: busy,
|
|
6187
|
+
onClick: async () => {
|
|
6188
|
+
if (busy || !onReconnect)
|
|
6189
|
+
return;
|
|
6190
|
+
setBusy(true);
|
|
6191
|
+
try {
|
|
6192
|
+
await onReconnect(ip);
|
|
6193
|
+
} finally {
|
|
6194
|
+
setBusy(false);
|
|
6195
|
+
}
|
|
6196
|
+
}
|
|
6197
|
+
},
|
|
6198
|
+
busy ? T.exitChecking || "checking\u2026" : label || T.exitReconnect || "Reconnect"
|
|
6199
|
+
);
|
|
6200
|
+
}
|
|
6201
|
+
function ExitCard({ T, region, activeExit, onSetExit, onReconnect, featureOn }) {
|
|
6058
6202
|
return /* @__PURE__ */ React.createElement("div", { style: { borderRadius: 11, border: "1px solid var(--line)", overflow: "hidden", background: "var(--panel)" } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "11px 14px", borderBottom: "1px solid var(--line)", background: "var(--panel-2)" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, letterSpacing: 0.5, padding: "2px 7px", borderRadius: 5, background: "var(--chip)", color: "var(--dim)" } }, region.flag), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--ui)", fontSize: 13.5, fontWeight: 600, color: "var(--text)" } }, region.label), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), (() => {
|
|
6059
6203
|
const reach = region.nodes.filter((n) => n.reachable).length;
|
|
6060
|
-
const
|
|
6061
|
-
|
|
6204
|
+
const failed = region.nodes.filter((n) => n.status === "failed").length;
|
|
6205
|
+
const down = region.nodes.length - reach - failed;
|
|
6206
|
+
return /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: down + failed > 0 ? "var(--warn)" : "var(--faint)" } }, reach, "/", region.nodes.length, " ", T.up, failed > 0 ? ` \xB7 ${failed} ${T.exitFailed || "failed"}` : "", down > 0 ? ` \xB7 ${down} down` : "");
|
|
6062
6207
|
})()), /* @__PURE__ */ React.createElement("div", null, region.nodes.map((n, i) => {
|
|
6063
6208
|
const active = n.ip === activeExit;
|
|
6064
6209
|
const stuck = n.online && !n.reachable;
|
|
6065
|
-
|
|
6210
|
+
const failed = n.status === "failed";
|
|
6211
|
+
const usable = n.reachable && featureOn !== false;
|
|
6212
|
+
return /* @__PURE__ */ React.createElement("div", { key: n.ip, className: "dk-wrap", style: { display: "flex", alignItems: "center", gap: 12, rowGap: 6, padding: "10px 14px", borderBottom: i < region.nodes.length - 1 ? "1px solid var(--line)" : "none", background: active ? "color-mix(in oklab, var(--accent), transparent 92%)" : "transparent" } }, /* @__PURE__ */ React.createElement(StatusDot, { online: n.reachable }), /* @__PURE__ */ React.createElement(Mono, { size: 13, copy: n.ip }, n.ip), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)" } }, n.host), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), n.reachable ? /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: n.ping > 200 ? "var(--warn)" : "var(--dim)" } }, n.ping, "ms") : failed ? /* @__PURE__ */ React.createElement(Tag, { tone: "danger", title: "Failed 3 checks in a row \u2014 no longer checked automatically" }, T.exitFailed || "failed") : n.status === "unknown" ? /* @__PURE__ */ React.createElement(Tag, { tone: "off" }, T.exitNotChecked || "not checked") : stuck ? /* @__PURE__ */ React.createElement(Tag, { tone: "warn", title: "Announces online but IP won't pass \u2014 session desync or NAT-blocked" }, "online \xB7 no route") : n.status === "retrying" ? /* @__PURE__ */ React.createElement(Tag, { tone: "warn", title: "No answer \u2014 will try again shortly" }, T.exitRetrying || "retrying", " \xB7 ", n.fails, "/3") : /* @__PURE__ */ React.createElement(Tag, { tone: "off" }, "offline"), failed ? /* @__PURE__ */ React.createElement(ReconnectBtn, { T, ip: n.ip, onReconnect }) : active ? /* @__PURE__ */ React.createElement(Btn, { tone: "ok", icon: "check", size: "sm", onClick: () => onSetExit(null) }, T.routing) : /* @__PURE__ */ React.createElement(Btn, { size: "sm", icon: "route", disabled: !usable, onClick: () => usable && onSetExit(n.ip), style: { opacity: usable ? 1 : 0.4 } }, T.routeThru));
|
|
6066
6213
|
})));
|
|
6067
6214
|
}
|
|
6068
6215
|
function LanEnableCard({ backend, onArm, onCancel }) {
|
|
@@ -6166,12 +6313,14 @@ function LanEnableCard({ backend, onArm, onCancel }) {
|
|
|
6166
6313
|
"\u53D6\u6D88,\u6062\u590D\u6D88\u606F"
|
|
6167
6314
|
)));
|
|
6168
6315
|
}
|
|
6169
|
-
function NetworkTab({ T, me, peers, exits, activeExit, reqCount, onSetExit, onOpenChat, backend, onArmLan, onCancelLan }) {
|
|
6316
|
+
function NetworkTab({ T, me, peers, exits, activeExit, exitFeature, routeFallback, onReconnectExit, reqCount, onSetExit, onOpenChat, backend, onArmLan, onCancelLan }) {
|
|
6317
|
+
const featureOn = !exitFeature || exitFeature.available !== false;
|
|
6318
|
+
const anyFailed = (exits || []).some((r) => (r.nodes || []).some((n) => n.status === "failed"));
|
|
6170
6319
|
if (backend && backend.switchable && !backend.hasVirtualLan) {
|
|
6171
6320
|
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { className: "dk-page", style: { maxWidth: 1040, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 26 } }, /* @__PURE__ */ React.createElement(MyNode, { T, me, activeExit, peers, reqCount }), /* @__PURE__ */ React.createElement(LanEnableCard, { backend, onArm: onArmLan, onCancel: onCancelLan })));
|
|
6172
6321
|
}
|
|
6173
6322
|
const lanless = !!backend && backend.hasVirtualLan === false;
|
|
6174
|
-
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { className: "dk-page", style: { maxWidth: 1040, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 26 } }, /* @__PURE__ */ React.createElement(MyNode, { T, me, activeExit, peers, reqCount }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement(Section, { label: T.peerRouting, count: peers.length }), /* @__PURE__ */ React.createElement(PeerTable, { T, peers, onOpenChat }), !peers.length && /* @__PURE__ */ React.createElement("div", { style: { padding: "14px 16px", borderRadius: 11, border: "1px dashed var(--line)", fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, T.noPeersYet || "No friends yet \u2014 add one from Chat and it shows up here with its route.")), lanless ? null : /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement(Section, { label: T.exitNodes, trailing: /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 6 } }, /* @__PURE__ */ React.createElement(Btn, { icon: "plus", size: "sm" }, T.addExit)) }), activeExit && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "11px 14px", borderRadius: 10, background: "color-mix(in oklab, var(--warn), transparent 90%)", border: "1px solid color-mix(in oklab, var(--warn), transparent 70%)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "route", size: 17, color: "var(--warn)", stroke: 2 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)" } }, T.egressVia), /* @__PURE__ */ React.createElement(Mono, { size: 13, copy: activeExit }, activeExit), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", icon: "unlink", size: "sm", onClick: () => onSetExit(null) }, T.stopRouting)), /* @__PURE__ */ React.createElement("div", { className: "dk-grid1", style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 } }, exits.map((r) => /* @__PURE__ */ React.createElement(ExitCard, { key: r.region, T, region: r, activeExit, onSetExit }))))));
|
|
6323
|
+
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { className: "dk-page", style: { maxWidth: 1040, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 26 } }, /* @__PURE__ */ React.createElement(MyNode, { T, me, activeExit, peers, reqCount }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement(Section, { label: T.peerRouting, count: peers.length }), /* @__PURE__ */ React.createElement(PeerTable, { T, peers, onOpenChat }), !peers.length && /* @__PURE__ */ React.createElement("div", { style: { padding: "14px 16px", borderRadius: 11, border: "1px dashed var(--line)", fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, T.noPeersYet || "No friends yet \u2014 add one from Chat and it shows up here with its route.")), lanless ? null : /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement(Section, { label: T.exitNodes, trailing: /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 6 } }, anyFailed && onReconnectExit && /* @__PURE__ */ React.createElement(ReconnectBtn, { T, ip: null, onReconnect: onReconnectExit, label: T.exitReconnectAll || "Reconnect all" }), /* @__PURE__ */ React.createElement(Btn, { icon: "plus", size: "sm" }, T.addExit)) }), !featureOn && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 4, padding: "11px 14px", borderRadius: 10, background: "var(--panel-2)", border: "1px dashed var(--line)" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, fontWeight: 700, color: "var(--text)" } }, T.noExitAvailable || "No exit available \u2014 routing and proxy are off."), anyFailed && /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--dim)" } }, T.noExitHint || "Every exit failed 3 times and is no longer checked automatically. Press Reconnect to try one again.")), routeFallback && routeFallback.from && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "11px 14px", borderRadius: 10, background: "color-mix(in oklab, var(--warn), transparent 90%)", border: "1px solid color-mix(in oklab, var(--warn), transparent 70%)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "route", size: 17, color: "var(--warn)", stroke: 2 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)" } }, T.routeFellBack || "Routing fell back to direct: every exit it used failed. Reconnect one to route through it again.")), activeExit && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "11px 14px", borderRadius: 10, background: "color-mix(in oklab, var(--warn), transparent 90%)", border: "1px solid color-mix(in oklab, var(--warn), transparent 70%)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "route", size: 17, color: "var(--warn)", stroke: 2 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)" } }, T.egressVia), /* @__PURE__ */ React.createElement(Mono, { size: 13, copy: activeExit }, activeExit), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", icon: "unlink", size: "sm", onClick: () => onSetExit(null) }, T.stopRouting)), /* @__PURE__ */ React.createElement("div", { className: "dk-grid1", style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 } }, exits.map((r) => /* @__PURE__ */ React.createElement(ExitCard, { key: r.region, T, region: r, activeExit, onSetExit, onReconnect: onReconnectExit, featureOn }))))));
|
|
6175
6324
|
}
|
|
6176
6325
|
Object.assign(window, { NetworkTab });
|
|
6177
6326
|
|
|
@@ -6512,6 +6661,9 @@ var STR = {
|
|
|
6512
6661
|
dirSearch: "search name / userid\u2026",
|
|
6513
6662
|
dirLoading: "loading directory\u2026",
|
|
6514
6663
|
noName: "no name given",
|
|
6664
|
+
friendRequestItem: "Friend request",
|
|
6665
|
+
showAll: "show all",
|
|
6666
|
+
showLess: "show less",
|
|
6515
6667
|
dirEmpty: "nobody matches",
|
|
6516
6668
|
dirError: "Could not load the directory:",
|
|
6517
6669
|
bridgeEmpty: "Nobody else is listed on this bridge yet. You are the first.",
|
|
@@ -6586,6 +6738,15 @@ var STR = {
|
|
|
6586
6738
|
routeThru: "route",
|
|
6587
6739
|
egressVia: "all egress routed via",
|
|
6588
6740
|
stopRouting: "stop",
|
|
6741
|
+
exitFailed: "failed",
|
|
6742
|
+
exitReconnect: "Reconnect",
|
|
6743
|
+
exitReconnectAll: "Reconnect all",
|
|
6744
|
+
exitChecking: "checking\u2026",
|
|
6745
|
+
exitNotChecked: "not checked",
|
|
6746
|
+
exitRetrying: "retrying",
|
|
6747
|
+
noExitAvailable: "No exit available \u2014 routing and proxy are off.",
|
|
6748
|
+
noExitHint: "Every exit failed 3 times and is no longer checked automatically. Press Reconnect to try one again.",
|
|
6749
|
+
routeFellBack: "Routing fell back to direct: every exit it used failed. Reconnect one to route through it again.",
|
|
6589
6750
|
identity: "Identity",
|
|
6590
6751
|
userId: "user id",
|
|
6591
6752
|
carrierAddr: "carrier address",
|
|
@@ -6711,6 +6872,9 @@ var STR = {
|
|
|
6711
6872
|
dirSearch: "\u641C\u7D22\u540D\u5B57 / userid\u2026",
|
|
6712
6873
|
dirLoading: "\u6B63\u5728\u52A0\u8F7D\u540D\u5355\u2026",
|
|
6713
6874
|
noName: "\u5BF9\u65B9\u6CA1\u6709\u586B\u540D\u5B57",
|
|
6875
|
+
friendRequestItem: "\u597D\u53CB\u8BF7\u6C42",
|
|
6876
|
+
showAll: "\u5C55\u5F00\u5168\u6587",
|
|
6877
|
+
showLess: "\u6536\u8D77",
|
|
6714
6878
|
dirEmpty: "\u6CA1\u6709\u5339\u914D\u7684\u4EBA",
|
|
6715
6879
|
dirError: "\u540D\u5355\u52A0\u8F7D\u5931\u8D25\uFF1A",
|
|
6716
6880
|
bridgeEmpty: "\u8FD9\u4E2A bridge \u4E0A\u8FD8\u6CA1\u6709\u522B\u4EBA\uFF0C\u4F60\u662F\u7B2C\u4E00\u4E2A\u3002",
|
|
@@ -6785,6 +6949,15 @@ var STR = {
|
|
|
6785
6949
|
routeThru: "\u8DEF\u7531",
|
|
6786
6950
|
egressVia: "\u5168\u90E8\u51FA\u53E3\u6D41\u91CF\u7ECF\u7531",
|
|
6787
6951
|
stopRouting: "\u505C\u6B62",
|
|
6952
|
+
exitFailed: "\u5DF2\u5931\u8D25",
|
|
6953
|
+
exitReconnect: "\u91CD\u65B0\u8FDE\u63A5",
|
|
6954
|
+
exitReconnectAll: "\u5168\u90E8\u91CD\u8FDE",
|
|
6955
|
+
exitChecking: "\u68C0\u6D4B\u4E2D\u2026",
|
|
6956
|
+
exitNotChecked: "\u672A\u68C0\u6D4B",
|
|
6957
|
+
exitRetrying: "\u91CD\u8BD5\u4E2D",
|
|
6958
|
+
noExitAvailable: "\u6CA1\u6709\u53EF\u7528\u7684\u51FA\u53E3 \u2014\u2014 \u8DEF\u7531\u548C\u4EE3\u7406\u5DF2\u5173\u95ED\u3002",
|
|
6959
|
+
noExitHint: "\u6240\u6709\u51FA\u53E3\u90FD\u8FDE\u7EED\u5931\u8D25 3 \u6B21,\u5DF2\u4E0D\u518D\u81EA\u52A8\u68C0\u6D4B\u3002\u70B9\u300C\u91CD\u65B0\u8FDE\u63A5\u300D\u518D\u8BD5\u4E00\u6B21\u3002",
|
|
6960
|
+
routeFellBack: "\u8DEF\u7531\u5DF2\u6539\u56DE\u76F4\u8FDE:\u5B83\u4F7F\u7528\u7684\u51FA\u53E3\u5168\u90E8\u5931\u8D25\u3002\u91CD\u65B0\u8FDE\u63A5\u5176\u4E2D\u4E00\u4E2A\u5373\u53EF\u6062\u590D\u3002",
|
|
6788
6961
|
identity: "\u8EAB\u4EFD",
|
|
6789
6962
|
userId: "\u7528\u6237 ID",
|
|
6790
6963
|
carrierAddr: "Carrier \u5730\u5740",
|
|
@@ -7079,7 +7252,7 @@ function DkApp() {
|
|
|
7079
7252
|
return hasTab(tabs, want) || DK_PEOPLE_TABS.includes(want) && showPeople ? want : firstTab;
|
|
7080
7253
|
});
|
|
7081
7254
|
const [activeId, setActiveId] = React.useState(null);
|
|
7082
|
-
const data = useDaemonData();
|
|
7255
|
+
const data = useDaemonData({ exits: tab === "network" });
|
|
7083
7256
|
const me = data.me;
|
|
7084
7257
|
const [upd, setUpd] = React.useState(null);
|
|
7085
7258
|
React.useEffect(() => {
|
|
@@ -7442,6 +7615,10 @@ function DkApp() {
|
|
|
7442
7615
|
const onEdit = (name, description) => dkApi.setProfile(name, description).then(data.refresh);
|
|
7443
7616
|
const onSetExit = () => {
|
|
7444
7617
|
};
|
|
7618
|
+
const onReconnectExit = (ip) => (ip ? dkApi.reconnectExit(ip) : dkApi.reconnectAllExits()).then((r) => {
|
|
7619
|
+
data.refresh();
|
|
7620
|
+
return r;
|
|
7621
|
+
});
|
|
7445
7622
|
const onOpenChat = (id) => {
|
|
7446
7623
|
setTab("chat");
|
|
7447
7624
|
openConversation(id);
|
|
@@ -7521,7 +7698,7 @@ function DkApp() {
|
|
|
7521
7698
|
fontWeight: 600,
|
|
7522
7699
|
background: tab === n.id ? "var(--accent)" : "transparent",
|
|
7523
7700
|
color: tab === n.id ? "#fff" : "var(--dim)"
|
|
7524
|
-
} }, /* @__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, protocolFix, 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(
|
|
7701
|
+
} }, /* @__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, protocolFix, 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, exitFeature: data.exitFeature, routeFallback: data.routeFallback, onReconnectExit, 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(
|
|
7525
7702
|
ProfileTab,
|
|
7526
7703
|
{
|
|
7527
7704
|
T,
|
package/package.json
CHANGED