@decentnetwork/lan 0.1.94 → 0.1.96
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/cli/index.js +8 -2
- 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/cli/index.js
CHANGED
|
@@ -142,10 +142,16 @@ async function main() {
|
|
|
142
142
|
})
|
|
143
143
|
.command("ui", "Start the local Friend UI web page (friends list, accept/reject requests, add friend)", (y) => y
|
|
144
144
|
.option("port", { type: "number", default: 8765, describe: "Local listen port" })
|
|
145
|
-
.option("
|
|
145
|
+
.option("host", {
|
|
146
|
+
type: "string",
|
|
147
|
+
default: "127.0.0.1",
|
|
148
|
+
// `--listen` kept as a hidden back-compat alias (older docs/commands).
|
|
149
|
+
alias: "listen",
|
|
150
|
+
describe: "Bind address (use 0.0.0.0 to reach it from other devices on your LAN)",
|
|
151
|
+
})
|
|
146
152
|
.option("dora-dir", { type: "string", describe: "If this node runs a dora server, its data-dir (to show allocations)" })
|
|
147
153
|
.option("config-dir", { type: "string" }), async (argv) => {
|
|
148
|
-
await cmdUi({ port: argv.port, listen: argv.listen, doraDir: argv["dora-dir"], configDir: argv["config-dir"] });
|
|
154
|
+
await cmdUi({ port: argv.port, listen: argv.host ?? argv.listen, doraDir: argv["dora-dir"], configDir: argv["config-dir"] });
|
|
149
155
|
})
|
|
150
156
|
// Tell the running daemon to re-exec itself with its original argv.
|
|
151
157
|
// The daemon inherits its own uid (root if it was launched as root)
|
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>
|