@decentnetwork/lan 0.1.95 → 0.1.97
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/bin/tun-helper-darwin-amd64 +0 -0
- package/bin/tun-helper-darwin-arm64 +0 -0
- package/bin/tun-helper-linux-amd64 +0 -0
- package/bin/tun-helper-linux-arm64 +0 -0
- package/dist/daemon/server.js +11 -1
- package/dist/ui/desktop/app.js +50 -16
- package/dist/ui/desktop/index.html +2 -0
- package/dist/ui/desktop/vendor/qrcode.js +2297 -0
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/daemon/server.js
CHANGED
|
@@ -180,7 +180,17 @@ export class DaemonServer {
|
|
|
180
180
|
keyFile,
|
|
181
181
|
bootstrapNodes: this.config.carrier.bootstrapNodes,
|
|
182
182
|
expressNodes: this.config.carrier.expressNodes ?? [],
|
|
183
|
-
|
|
183
|
+
// Express must stay enabled for the CHAT data plane (sendText, packet 64)
|
|
184
|
+
// so messages still reach a native iPad/Android friend whose Carrier
|
|
185
|
+
// session is down — their relay session flaps constantly under iOS
|
|
186
|
+
// suspension (see peer docs/IOS_INTEROP_PLAYBOOK.md §3d), and without the
|
|
187
|
+
// express store-and-forward our replies just throw "friend is offline".
|
|
188
|
+
// The IP data plane never touches express anyway: decentlan sends IP via
|
|
189
|
+
// sendCustomPacket (161/162/163), which goes straight to the messenger
|
|
190
|
+
// and fails fast when offline — so it needs no gate. The dora session-kick
|
|
191
|
+
// flood this used to guard against is already neutralised by the SDK
|
|
192
|
+
// dropping empty sendText before express (peer.ts). So: keep express on.
|
|
193
|
+
expressControlPlaneOnly: false,
|
|
184
194
|
// Advertise this node's name so friends see "cn"/"tokyo"/"mac-dev"
|
|
185
195
|
// instead of the generic "@decentnetwork/peer".
|
|
186
196
|
nickname: this.config.node.name,
|
package/dist/ui/desktop/app.js
CHANGED
|
@@ -155,6 +155,29 @@ async function dkPost(path, body) {
|
|
|
155
155
|
return { ok: r.ok };
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
|
+
function dkCopy(text) {
|
|
159
|
+
const s = text == null ? "" : String(text);
|
|
160
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
161
|
+
return navigator.clipboard.writeText(s).then(() => true).catch(() => dkCopyLegacy(s));
|
|
162
|
+
}
|
|
163
|
+
return Promise.resolve(dkCopyLegacy(s));
|
|
164
|
+
}
|
|
165
|
+
function dkCopyLegacy(s) {
|
|
166
|
+
try {
|
|
167
|
+
const ta = document.createElement("textarea");
|
|
168
|
+
ta.value = s;
|
|
169
|
+
ta.setAttribute("readonly", "");
|
|
170
|
+
ta.style.cssText = "position:fixed;top:-1000px;left:-1000px;opacity:0";
|
|
171
|
+
document.body.appendChild(ta);
|
|
172
|
+
ta.select();
|
|
173
|
+
ta.setSelectionRange(0, s.length);
|
|
174
|
+
const ok = document.execCommand("copy");
|
|
175
|
+
document.body.removeChild(ta);
|
|
176
|
+
return ok;
|
|
177
|
+
} catch (e) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
158
181
|
const dkApi = {
|
|
159
182
|
send: (userid, text) => dkPost("/api/chat-send", { userid, text }),
|
|
160
183
|
add: (address) => dkPost("/api/add", { address }),
|
|
@@ -232,7 +255,8 @@ Object.assign(window, {
|
|
|
232
255
|
dkDayLabel,
|
|
233
256
|
dkApi,
|
|
234
257
|
useDaemonData,
|
|
235
|
-
DK_ME_FALLBACK
|
|
258
|
+
DK_ME_FALLBACK,
|
|
259
|
+
dkCopy
|
|
236
260
|
});
|
|
237
261
|
function DkIdenticon({ seed, size = 30, radius = 7 }) {
|
|
238
262
|
const { cells, hue } = dkIdenticon(seed);
|
|
@@ -282,10 +306,7 @@ function Mono({ children, dim, size = 12.5, copy, title }) {
|
|
|
282
306
|
const [hit, setHit] = React.useState(false);
|
|
283
307
|
const onCopy = (e) => {
|
|
284
308
|
e.stopPropagation();
|
|
285
|
-
|
|
286
|
-
navigator.clipboard.writeText(copy === true ? String(children) : copy);
|
|
287
|
-
} catch (e2) {
|
|
288
|
-
}
|
|
309
|
+
dkCopy(copy === true ? String(children) : copy);
|
|
289
310
|
setHit(true);
|
|
290
311
|
setTimeout(() => setHit(false), 900);
|
|
291
312
|
};
|
|
@@ -1046,12 +1067,7 @@ function StatTile({ label, value, sub, tone }) {
|
|
|
1046
1067
|
function MyNode({ T, me, activeExit, peers, reqCount = 0 }) {
|
|
1047
1068
|
const online = peers.filter((p) => p.online).length;
|
|
1048
1069
|
const direct = peers.filter((p) => p.online && p.via === "direct").length;
|
|
1049
|
-
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14, padding: "16px 18px", borderRadius: 12, background: "var(--panel)", border: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(DkIdenticon, { seed: me.userId, size: 46, radius: 11 }), /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", right: -3, bottom: -3, width: 14, height: 14, borderRadius: 999, background: "var(--online)", border: "2.5px solid var(--panel)" } })), /* @__PURE__ */ React.createElement("div", { style: { minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 9 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 17, fontWeight: 700, color: "var(--text)" } }, me.name), /* @__PURE__ */ React.createElement(Tag, { tone: "ok" }, "online"), /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, me.channel), me.isExit && /* @__PURE__ */ React.createElement(Tag, { tone: "warn" }, "exit", me.exitRegion ? ` \xB7 ${me.exitRegion.toUpperCase()}` : "")), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 5 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, T.myIp), /* @__PURE__ */ React.createElement(Mono, { size: 13, copy: me.ip }, me.ip), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, "wire ", me.wire), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, "lan ", me.lanVer, " / peer ", me.peerVer))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement(Btn, { icon: "copy", onClick: () => {
|
|
1050
|
-
try {
|
|
1051
|
-
navigator.clipboard.writeText(me.carrier);
|
|
1052
|
-
} catch (e) {
|
|
1053
|
-
}
|
|
1054
|
-
} }, T.copyAddr)), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 12 } }, /* @__PURE__ */ React.createElement(StatTile, { label: T.peersOnline, value: `${online}/${peers.length}`, sub: `${direct} ${T.direct}`, tone: "var(--online)" }), /* @__PURE__ */ React.createElement(StatTile, { label: T.activeEgress, value: activeExit ? activeExit : T.directEgress, sub: activeExit ? "china \xB7 cn-sh-01" : T.noProxy, tone: activeExit ? "var(--warn)" : "var(--text)" }), /* @__PURE__ */ React.createElement(StatTile, { label: T.wireLabel, value: me.wire, sub: `${me.channel} \xB7 ${T.lossless}` }), /* @__PURE__ */ React.createElement(StatTile, { label: T.reqs, value: String(reqCount), sub: T.pending, tone: reqCount ? "var(--accent)" : "var(--text)" })));
|
|
1070
|
+
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14, padding: "16px 18px", borderRadius: 12, background: "var(--panel)", border: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(DkIdenticon, { seed: me.userId, size: 46, radius: 11 }), /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", right: -3, bottom: -3, width: 14, height: 14, borderRadius: 999, background: "var(--online)", border: "2.5px solid var(--panel)" } })), /* @__PURE__ */ React.createElement("div", { style: { minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 9 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 17, fontWeight: 700, color: "var(--text)" } }, me.name), /* @__PURE__ */ React.createElement(Tag, { tone: "ok" }, "online"), /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, me.channel), me.isExit && /* @__PURE__ */ React.createElement(Tag, { tone: "warn" }, "exit", me.exitRegion ? ` \xB7 ${me.exitRegion.toUpperCase()}` : "")), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 5 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, T.myIp), /* @__PURE__ */ React.createElement(Mono, { size: 13, copy: me.ip }, me.ip), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, "wire ", me.wire), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, "lan ", me.lanVer, " / peer ", me.peerVer))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement(Btn, { icon: "copy", onClick: () => dkCopy(me.carrier) }, T.copyAddr)), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 12 } }, /* @__PURE__ */ React.createElement(StatTile, { label: T.peersOnline, value: `${online}/${peers.length}`, sub: `${direct} ${T.direct}`, tone: "var(--online)" }), /* @__PURE__ */ React.createElement(StatTile, { label: T.activeEgress, value: activeExit ? activeExit : T.directEgress, sub: activeExit ? "china \xB7 cn-sh-01" : T.noProxy, tone: activeExit ? "var(--warn)" : "var(--text)" }), /* @__PURE__ */ React.createElement(StatTile, { label: T.wireLabel, value: me.wire, sub: `${me.channel} \xB7 ${T.lossless}` }), /* @__PURE__ */ React.createElement(StatTile, { label: T.reqs, value: String(reqCount), sub: T.pending, tone: reqCount ? "var(--accent)" : "var(--text)" })));
|
|
1055
1071
|
}
|
|
1056
1072
|
function PeerTable({ T, peers, onOpenChat }) {
|
|
1057
1073
|
return /* @__PURE__ */ React.createElement("div", { style: { borderRadius: 11, border: "1px solid var(--line)", overflow: "hidden", background: "var(--panel)" } }, /* @__PURE__ */ React.createElement("div", { style: { display: "grid", gridTemplateColumns: "1.4fr 1fr 1fr 0.8fr 90px", gap: 0, padding: "9px 16px", borderBottom: "1px solid var(--line)", background: "var(--panel-2)" } }, [T.colPeer, T.colVip, T.colPath, T.colWire, ""].map((h, i) => /* @__PURE__ */ React.createElement("span", { key: i, style: { fontFamily: "var(--mono)", fontSize: 10.5, fontWeight: 700, letterSpacing: 0.8, textTransform: "uppercase", color: "var(--faint)", textAlign: i === 4 ? "right" : "left" } }, h))), peers.map((p, i) => /* @__PURE__ */ React.createElement("div", { key: p.id, style: {
|
|
@@ -1073,19 +1089,37 @@ function NetworkTab({ T, me, peers, exits, activeExit, reqCount, onSetExit, onOp
|
|
|
1073
1089
|
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { 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 })), /* @__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", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 } }, exits.map((r) => /* @__PURE__ */ React.createElement(ExitCard, { key: r.region, T, region: r, activeExit, onSetExit }))))));
|
|
1074
1090
|
}
|
|
1075
1091
|
Object.assign(window, { NetworkTab });
|
|
1076
|
-
function FieldRow({ label, value, mono = true, copy, qr, last }) {
|
|
1077
|
-
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14, padding: "13px 16px", borderBottom: last ? "none" : "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("span", { style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, label), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: mono ? "var(--mono)" : "var(--ui)", fontSize: 13.5, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, value), copy && /* @__PURE__ */ React.createElement(Btn, { icon: "copy", size: "sm", onClick: () => {
|
|
1092
|
+
function FieldRow({ label, value, mono = true, copy, qr, onQr, last }) {
|
|
1093
|
+
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14, padding: "13px 16px", borderBottom: last ? "none" : "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("span", { style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, label), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: mono ? "var(--mono)" : "var(--ui)", fontSize: 13.5, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, value), copy && /* @__PURE__ */ React.createElement(Btn, { icon: "copy", size: "sm", onClick: () => dkCopy(value) }), qr && /* @__PURE__ */ React.createElement(Btn, { icon: "qr", size: "sm", onClick: () => onQr && onQr(value, label) }));
|
|
1094
|
+
}
|
|
1095
|
+
function DkQrModal({ value, label, onClose }) {
|
|
1096
|
+
const qr = React.useMemo(() => {
|
|
1097
|
+
if (typeof qrcode === "undefined") return null;
|
|
1078
1098
|
try {
|
|
1079
|
-
|
|
1099
|
+
const q = qrcode(0, "M");
|
|
1100
|
+
q.addData(value);
|
|
1101
|
+
q.make();
|
|
1102
|
+
return q;
|
|
1080
1103
|
} catch (e) {
|
|
1104
|
+
return null;
|
|
1081
1105
|
}
|
|
1082
|
-
}
|
|
1106
|
+
}, [value]);
|
|
1107
|
+
const count = qr ? qr.getModuleCount() : 0;
|
|
1108
|
+
const quiet = 4;
|
|
1109
|
+
const total = count + quiet * 2;
|
|
1110
|
+
const px = 248;
|
|
1111
|
+
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: 24 } }, /* @__PURE__ */ React.createElement("div", { onClick: (e) => e.stopPropagation(), style: { background: "var(--panel)", border: "1px solid var(--line)", borderRadius: 16, padding: 22, display: "flex", flexDirection: "column", alignItems: "center", gap: 16, maxWidth: 340 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, alignSelf: "stretch" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 700, letterSpacing: 0.5, textTransform: "uppercase", color: "var(--faint)" } }, label), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement(Btn, { icon: "x", size: "sm", onClick: onClose })), qr ? /* @__PURE__ */ React.createElement("svg", { width: px, height: px, viewBox: `0 0 ${total} ${total}`, shapeRendering: "crispEdges", style: { display: "block", borderRadius: 10, background: "#fff" } }, /* @__PURE__ */ React.createElement("rect", { width: total, height: total, fill: "#fff" }), Array.from({ length: count }).map(
|
|
1112
|
+
(_, r) => Array.from({ length: count }).map(
|
|
1113
|
+
(__, c) => qr.isDark(r, c) ? /* @__PURE__ */ React.createElement("rect", { key: `${r}-${c}`, x: c + quiet, y: r + quiet, width: 1.04, height: 1.04, fill: "#000" }) : null
|
|
1114
|
+
)
|
|
1115
|
+
)) : /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)", padding: 48 } }, "QR unavailable"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--dim)", wordBreak: "break-all", textAlign: "center", maxWidth: 280, lineHeight: 1.5 } }, value), /* @__PURE__ */ React.createElement(Btn, { icon: "copy", size: "sm", onClick: () => dkCopy(value) }, "copy")));
|
|
1083
1116
|
}
|
|
1084
1117
|
function Card({ label, children, trailing }) {
|
|
1085
1118
|
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 10 } }, /* @__PURE__ */ React.createElement(Section, { label, trailing }), /* @__PURE__ */ React.createElement("div", { style: { borderRadius: 11, border: "1px solid var(--line)", overflow: "hidden", background: "var(--panel)" } }, children));
|
|
1086
1119
|
}
|
|
1087
1120
|
function ProfileTab({ T, me }) {
|
|
1088
|
-
|
|
1121
|
+
const [qr, setQr] = React.useState(null);
|
|
1122
|
+
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: 24 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 18, padding: "20px 22px", borderRadius: 14, background: "var(--panel)", border: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(DkIdenticon, { seed: me.userId, size: 68, radius: 16 }), /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", right: -3, bottom: -3, width: 18, height: 18, borderRadius: 999, background: "var(--online)", border: "3px solid var(--panel)" } })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 22, fontWeight: 700, letterSpacing: -0.5, color: "var(--text)" } }, me.name), /* @__PURE__ */ React.createElement(Tag, { tone: "ok" }, "online"), me.isExit && /* @__PURE__ */ React.createElement(Tag, { tone: "warn" }, "exit", me.exitRegion ? ` \xB7 ${me.exitRegion.toUpperCase()}` : "")), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13, color: "var(--dim)", marginTop: 4 } }, me.handle)), /* @__PURE__ */ React.createElement(Btn, { icon: "edit" }, T.editProfile)), /* @__PURE__ */ React.createElement(Card, { label: T.identity }, /* @__PURE__ */ React.createElement(FieldRow, { label: T.userId, value: me.userId, copy: true, qr: true, onQr: (v, l) => setQr({ value: v, label: l }) }), /* @__PURE__ */ React.createElement(FieldRow, { label: T.carrierAddr, value: me.carrier, copy: true, qr: true, onQr: (v, l) => setQr({ value: v, label: l }) }), /* @__PURE__ */ React.createElement(FieldRow, { label: T.netKey, value: me.netKey, copy: true, last: true })), /* @__PURE__ */ React.createElement(Card, { label: T.network }, /* @__PURE__ */ React.createElement(FieldRow, { label: T.virtualIp, value: me.ip, copy: true }), /* @__PURE__ */ React.createElement(FieldRow, { label: T.wireLabel, value: `${me.wire} \xB7 lossless`, mono: false }), /* @__PURE__ */ React.createElement(FieldRow, { label: T.version, value: `lan ${me.lanVer} \xB7 peer ${me.peerVer} \xB7 ${me.channel}`, last: true })), /* @__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, { value: qr.value, label: qr.label, onClose: () => setQr(null) }));
|
|
1089
1123
|
}
|
|
1090
1124
|
Object.assign(window, { ProfileTab });
|
|
1091
1125
|
const DK_DEFAULTS = (
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
<!-- Vendored React (no CDN — works offline / behind the GFW). -->
|
|
31
31
|
<script src="vendor/react.production.min.js"></script>
|
|
32
32
|
<script src="vendor/react-dom.production.min.js"></script>
|
|
33
|
+
<!-- QR encoder (Kazuhiko Arase, MIT) — exposes window.qrcode for the QR-code button. -->
|
|
34
|
+
<script src="vendor/qrcode.js"></script>
|
|
33
35
|
<!-- The whole desktop app, esbuild-transpiled from src/ui/desktop/*.jsx. -->
|
|
34
36
|
<script src="app.js"></script>
|
|
35
37
|
</body>
|