Release 4.1.2+1: настоящие SVG-флаги стран — Windows/WebView2 не отображает эмодзи-флаги (Segoe UI Emoji рисует пары региональных индикаторов буквами «BE», «LV»), поэтому встроен офлайн-генератор SVG-флагов (~75 стран: полосы/кресты + упрощённые сложные флаги GB/US/CH/KR и др., ~6 КБ в index.html); флаги в списке серверов и на карточке активного сервера, буквенный код — только для неизвестных регионов, обе темы. Windows 4.1.2.1.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Navis
2026-08-01 22:04:18 +03:00
co-authored by Cursor
parent 46e28aa73f
commit 74ffbd87d7
13 changed files with 185 additions and 34 deletions
+2 -2
View File
@@ -13,8 +13,8 @@ android {
minSdk = 26
targetSdk = 35
// versionCode = major*1_000_000 + minor*10_000 + patch*100 + build
versionCode = 4_010_101
versionName = "4.1.1+1"
versionCode = 4_010_201
versionName = "4.1.2+1"
vectorDrawables.useSupportLibrary = true
ndk {
abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64")
+4 -4
View File
@@ -1,16 +1,16 @@
{
"version": "4.1.1",
"notes": "4.1.1: исправлено отображение трафика, улучшены карточки провайдеров и список серверов",
"version": "4.1.2",
"notes": "4.1.2: настоящие флаги стран (SVG) — на Windows эмодзи-флаги не отображаются",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "362be966c08b95991deb3d7f47c839493fa1ef4cbc0f0325fb50f3d40b1f6225",
"sha256": "af18f836ec6607cc151ba373d2186548ba5d2033f4366445264285e92d370bee",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "362be966c08b95991deb3d7f47c839493fa1ef4cbc0f0325fb50f3d40b1f6225",
"sha256": "af18f836ec6607cc151ba373d2186548ba5d2033f4366445264285e92d370bee",
"os": "windows",
"arch": "amd64"
},
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+4 -4
View File
@@ -1,16 +1,16 @@
{
"version": "4.1.1",
"notes": "4.1.1: исправлено отображение трафика, улучшены карточки провайдеров и список серверов",
"version": "4.1.2",
"notes": "4.1.2: настоящие флаги стран (SVG) — на Windows эмодзи-флаги не отображаются",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "362be966c08b95991deb3d7f47c839493fa1ef4cbc0f0325fb50f3d40b1f6225",
"sha256": "af18f836ec6607cc151ba373d2186548ba5d2033f4366445264285e92d370bee",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "362be966c08b95991deb3d7f47c839493fa1ef4cbc0f0325fb50f3d40b1f6225",
"sha256": "af18f836ec6607cc151ba373d2186548ba5d2033f4366445264285e92d370bee",
"os": "windows",
"arch": "amd64"
},
+163 -12
View File
@@ -832,6 +832,11 @@
border: 1px solid var(--line);
}
.server-row .flag.unknown { font-size: .7rem; font-weight: 700; color: var(--muted); }
/* Настоящие SVG-флаги: Windows не отображает эмодзи-флаги (Segoe UI Emoji
рисует пары региональных индикаторов буквами «BE», «LV» и т.п.). */
.flag svg.fl { display: block; width: 22px; height: 15px; border-radius: 3px; box-shadow: 0 0 0 1px rgba(127,127,127,.3); }
.cur-server .flag svg.fl { width: 28px; height: 19px; border-radius: 4px; }
.cur-server .flag.unknown { font-size: .78rem; font-weight: 700; color: var(--muted); }
.server-row .meta-col { min-width: 0; }
.server-row .name {
font-weight: 700;
@@ -1233,7 +1238,7 @@
<div class="brand-row">
<div class="logo" aria-hidden="true"><span class="fox">🦊</span></div>
<h1 class="brand">EvilFox</h1>
<span class="badge-ver" id="badgeVer">4.1.1</span>
<span class="badge-ver" id="badgeVer">4.1.2</span>
</div>
<button class="nav-btn active" type="button" data-page="guard">
@@ -1711,11 +1716,159 @@
const c = String(cc || "").toUpperCase();
return COUNTRY_RU[c] || "";
}
// "RO" → "🇷🇴" — запасной вариант, если бэкенд не прислал флаг.
function isoFlag(cc) {
const c = String(cc || "").toUpperCase();
if (!/^[A-Z]{2}$/.test(c)) return "";
return String.fromCodePoint(0x1f1e6 + c.charCodeAt(0) - 65, 0x1f1e6 + c.charCodeAt(1) - 65);
// ==== SVG-флаги ====
// Windows не умеет эмодзи-флаги: Segoe UI Emoji рисует пары региональных
// индикаторов буквами («BE», «LV»), поэтому рисуем настоящие SVG-флаги.
// Формат описателя: h — горизонтальные полосы (цвет или [цвет, вес]),
// v — вертикальные полосы, n — скандинавский крест [фон, крест, внутренний],
// s — произвольный SVG-фрагмент, e — элементы поверх полос.
function star5(cx, cy, r, fill) {
let d = "";
for (let i = 0; i < 10; i++) {
const a = -Math.PI / 2 + (i * Math.PI) / 5;
const rr = i % 2 ? r * 0.4 : r;
d += (i ? "L" : "M") + (cx + rr * Math.cos(a)).toFixed(1) + " " + (cy + rr * Math.sin(a)).toFixed(1);
}
return '<path d="' + d + 'Z" fill="' + fill + '"/>';
}
// Упрощённый Юнион Джек (полный флаг и малый кантон для AU/NZ).
const UJ = '<rect width="60" height="40" fill="#012169"/><path d="M0 0 60 40M60 0 0 40" stroke="#FFF" stroke-width="8"/><path d="M0 0 60 40M60 0 0 40" stroke="#C8102E" stroke-width="3.5"/><path d="M30 0V40M0 20H60" stroke="#FFF" stroke-width="13"/><path d="M30 0V40M0 20H60" stroke="#C8102E" stroke-width="7"/>';
const UJC = '<path d="M0 0 30 20M30 0 0 20" stroke="#FFF" stroke-width="4"/><path d="M15 0V20M0 10H30" stroke="#FFF" stroke-width="6"/><path d="M15 0V20M0 10H30" stroke="#C8102E" stroke-width="3"/>';
const FLAGS = {
AT: { h: ["#ED2939", "#FFF", "#ED2939"] },
BE: { v: ["#000", "#FDDA24", "#EF3340"] },
BG: { h: ["#FFF", "#00966E", "#D62612"] },
CH: { s: '<rect width="60" height="40" fill="#DA291C"/><rect x="26" y="8" width="8" height="24" fill="#FFF"/><rect x="18" y="16" width="24" height="8" fill="#FFF"/>' },
CZ: { h: ["#FFF", "#D7141A"], e: '<path d="M0 0 30 20 0 40Z" fill="#11457E"/>' },
DE: { h: ["#000", "#DD0000", "#FFCE00"] },
DK: { n: ["#C8102E", "#FFF"] },
EE: { h: ["#0072CE", "#000", "#FFF"] },
ES: { h: [["#AA151B", 1], ["#F1BF00", 2], ["#AA151B", 1]], e: '<rect x="12" y="16" width="6" height="8" rx="1.2" fill="#AA151B"/>' },
FI: { n: ["#FFF", "#002F6C"] },
FR: { v: ["#002395", "#FFF", "#ED2939"] },
GB: { s: UJ },
GR: { h: ["#0D5EAF", "#FFF", "#0D5EAF", "#FFF", "#0D5EAF", "#FFF", "#0D5EAF", "#FFF", "#0D5EAF"], e: '<rect width="22" height="22.5" fill="#0D5EAF"/><rect x="8.8" width="4.4" height="22.5" fill="#FFF"/><rect y="9" width="22" height="4.4" fill="#FFF"/>' },
HU: { h: ["#CE2939", "#FFF", "#477050"] },
IE: { v: ["#169B62", "#FFF", "#FF883E"] },
IT: { v: ["#008C45", "#F4F9F0", "#CD212A"] },
LT: { h: ["#FDB913", "#006A44", "#C1272D"] },
LU: { h: ["#ED2939", "#FFF", "#00A3E0"] },
LV: { h: [["#9E3039", 2], ["#FFF", 1], ["#9E3039", 2]] },
MD: { v: ["#003DA5", "#FFD100", "#C8102E"], e: '<circle cx="30" cy="20" r="4" fill="#B07F55"/>' },
NL: { h: ["#AE1C28", "#FFF", "#21468B"] },
NO: { n: ["#BA0C2F", "#FFF", "#00205B"] },
PL: { h: ["#FFF", "#DC143C"] },
PT: { v: [["#046A38", 2], ["#DA291C", 3]], e: '<circle cx="24" cy="20" r="5" fill="#FFE900" stroke="#DA291C" stroke-width="1.5"/>' },
RO: { v: ["#002B7F", "#FCD116", "#CE1126"] },
RS: { h: ["#C6363C", "#0C4076", "#FFF"] },
SE: { n: ["#006AA7", "#FECC00"] },
SI: { h: ["#FFF", "#005DA4", "#ED1C24"], e: '<path d="M14 8l4.5 7.5L14 22l-4.5-6.5Z" fill="#005DA4" stroke="#FFF" stroke-width="1"/>' },
SK: { h: ["#FFF", "#0B4EA2", "#EE1C25"], e: '<path d="M14 11l7 2.5v7c0 4.5-3.5 7-7 9-3.5-2-7-4.5-7-9v-7Z" fill="#EE1C25" stroke="#FFF" stroke-width="1.4"/>' },
TR: { s: '<rect width="60" height="40" fill="#E30A17"/><circle cx="23" cy="20" r="10" fill="#FFF"/><circle cx="25.5" cy="20" r="8" fill="#E30A17"/>' + star5(37, 20, 5, "#FFF") },
UA: { h: ["#0057B7", "#FFD700"] },
US: {
h: ["#B22234", "#FFF", "#B22234", "#FFF", "#B22234", "#FFF", "#B22234"],
e: '<rect width="26" height="17.2" fill="#3C3B6E"/>' + [[4, 4], [10, 4], [16, 4], [22, 4], [7, 8.6], [13, 8.6], [19, 8.6], [4, 13.2], [10, 13.2], [16, 13.2], [22, 13.2]].map(function (p) { return '<circle cx="' + p[0] + '" cy="' + p[1] + '" r="1.3" fill="#FFF"/>'; }).join("")
},
CA: { v: [["#D80621", 1], ["#FFF", 2], ["#D80621", 1]], e: '<path d="M30 9l2.2 4.4 4-1.8-1.3 5.4 4.4.9-3.1 3.5 3.6 2.7-5.4-.4.4 5.3-4.8-2.7-4.8 2.7.4-5.3-5.4.4 3.6-2.7-3.1-3.5 4.4-.9-1.3-5.4 4 1.8Z" fill="#D80621"/>' },
JP: { s: '<rect width="60" height="40" fill="#FFF"/><circle cx="30" cy="20" r="10" fill="#BC002D"/>' },
SG: { h: ["#EF3340", "#FFF"], e: '<circle cx="13" cy="10" r="5.5" fill="#FFF"/><circle cx="15.5" cy="10" r="5" fill="#EF3340"/>' },
HK: { s: '<rect width="60" height="40" fill="#DE2910"/>' + [0, 72, 144, 216, 288].map(function (a) { return '<ellipse cx="30" cy="13" rx="2.6" ry="7" fill="#FFF" transform="rotate(' + a + ' 30 20)"/>'; }).join("") },
AU: { s: '<rect width="60" height="40" fill="#012169"/>' + UJC + star5(15, 30, 4, "#FFF") + star5(45, 8, 3, "#FFF") + star5(38, 20, 3, "#FFF") + star5(52, 16, 3, "#FFF") + star5(45, 32, 3, "#FFF") },
NZ: { s: '<rect width="60" height="40" fill="#012169"/>' + UJC + star5(45, 8, 3, "#C8102E") + star5(38, 19, 3, "#C8102E") + star5(52, 19, 3, "#C8102E") + star5(45, 31, 3, "#C8102E") },
IS: { n: ["#02529C", "#FFF", "#DC1E35"] },
AL: { s: '<rect width="60" height="40" fill="#E41E20"/><path d="M30 8c-3 4-8 4.5-12 2.5 2 3.5 5.5 5.5 9 5.5-3 2.5-6.5 3.5-10 3 3 3.5 7.5 4.5 11 2.5v7l2 4 2-4v-7c3.5 2 8 1 11-2.5-3.5.5-7-.5-10-3 3.5 0 7-2 9-5.5-4 2-9 1.5-12-2.5Z" fill="#000"/>' },
MK: { s: '<rect width="60" height="40" fill="#D20000"/><path d="M30 20 0 0M30 20 60 0M30 20 0 40M30 20 60 40M30 20 0 20M30 20 60 20M30 20 30 0M30 20 30 40" stroke="#FFE600" stroke-width="4.5"/><circle cx="30" cy="20" r="7" fill="#FFE600" stroke="#D20000" stroke-width="1.5"/>' },
ME: { s: '<rect width="60" height="40" fill="#C40308"/><rect x="2" y="2" width="56" height="36" fill="none" stroke="#D3AE3B" stroke-width="3"/><circle cx="30" cy="20" r="6" fill="#D3AE3B"/>' },
BA: { s: '<rect width="60" height="40" fill="#002F6C"/><path d="M21 0h26L21 40Z" fill="#FECB00"/>' + star5(16, 6, 2.6, "#FFF") + star5(12, 16, 2.6, "#FFF") + star5(8, 26, 2.6, "#FFF") + star5(4, 36, 2.6, "#FFF") },
HR: { h: ["#C8102E", "#FFF", "#012169"], e: '<rect x="24" y="12" width="12" height="12" fill="#FFF" stroke="#C8102E" stroke-width="1"/><rect x="24" y="12" width="4" height="4" fill="#C8102E"/><rect x="32" y="12" width="4" height="4" fill="#C8102E"/><rect x="28" y="16" width="4" height="4" fill="#C8102E"/><rect x="24" y="20" width="4" height="4" fill="#C8102E"/><rect x="32" y="20" width="4" height="4" fill="#C8102E"/>' },
CY: { s: '<rect width="60" height="40" fill="#FFF"/><ellipse cx="30" cy="16" rx="13" ry="5" fill="#D57800"/><path d="M23 26c4.5 2.5 9.5 2.5 14 0" stroke="#4E5B31" stroke-width="2.2" fill="none"/>' },
MT: { s: '<rect width="30" height="40" fill="#FFF"/><rect x="30" width="30" height="40" fill="#CF142B"/><path d="M7 5h4v4h4v4h-4v4H7v-4H3V9h4Z" fill="#9CA3AF"/>' },
AM: { h: ["#D90012", "#0033A0", "#F2A800"] },
AZ: { h: ["#0092BC", "#E4002B", "#00AF66"], e: '<circle cx="26" cy="20" r="5" fill="#FFF"/><circle cx="28" cy="20" r="4.2" fill="#E4002B"/>' + star5(34, 20, 2.6, "#FFF") },
GE: { s: '<rect width="60" height="40" fill="#FFF"/><path d="M26 0h8v16h26v8H34v16h-8V24H0v-8h26Z" fill="#F04923"/><path d="M13 6v6M10 9h6M47 6v6M44 9h6M13 28v6M10 31h6M47 28v6M44 31h6" stroke="#F04923" stroke-width="2.4"/>' },
KZ: { s: '<rect width="60" height="40" fill="#00AFCA"/><circle cx="30" cy="17" r="7" fill="#FEC50C"/><path d="M17 29c8.5 3.5 17.5 3.5 26 0" stroke="#FEC50C" stroke-width="2.6" fill="none"/>' },
KG: { s: '<rect width="60" height="40" fill="#E8112D"/><circle cx="30" cy="20" r="8.5" fill="#FFEF00"/><path d="M24.5 21.5c1.5-4 9.5-4 11 0" stroke="#E8112D" stroke-width="2" fill="none"/>' },
UZ: { h: [["#0099B5", 10], ["#CE1126", 1.4], ["#FFF", 7.2], ["#CE1126", 1.4], ["#1EB53A", 10]], e: '<circle cx="10" cy="6.5" r="3.6" fill="#FFF"/><circle cx="11.6" cy="6.2" r="3.1" fill="#0099B5"/>' },
TM: { s: '<rect width="60" height="40" fill="#00843D"/><rect x="6" width="9" height="40" fill="#C8102E"/><circle cx="34" cy="12" r="5" fill="#FFF"/><circle cx="36" cy="11.2" r="4.3" fill="#00843D"/>' + star5(44, 10, 2.4, "#FFF") },
TJ: { h: [["#CC0000", 2], ["#FFF", 3], ["#006600", 2]], e: '<circle cx="30" cy="20" r="3" fill="#F8C300"/>' },
RU: { h: ["#FFF", "#0039A6", "#D52B1E"] },
BY: { s: '<rect width="60" height="26.7" fill="#CE1720"/><rect y="26.7" width="60" height="13.3" fill="#007C30"/><rect width="8" height="40" fill="#FFF"/><path d="M4 2l2.5 3L4 8 1.5 5Zm0 10l2.5 3L4 18l-2.5-3Zm0 10l2.5 3L4 28l-2.5-3Zm0 10l2.5 3L4 38l-2.5-3Z" fill="#CE1720"/>' },
IL: { s: '<rect width="60" height="40" fill="#FFF"/><rect y="5" width="60" height="5" fill="#0038B8"/><rect y="30" width="60" height="5" fill="#0038B8"/><path d="M30 12.5l6.5 11.2h-13Zm0 15l-6.5-11.2h13Z" fill="none" stroke="#0038B8" stroke-width="1.8"/>' },
AE: { s: '<rect width="60" height="13.4" fill="#00732F"/><rect y="13.4" width="60" height="13.3" fill="#FFF"/><rect y="26.6" width="60" height="13.4" fill="#000"/><rect width="16" height="40" fill="#FF0000"/>' },
SA: { s: '<rect width="60" height="40" fill="#006C35"/><rect x="12" y="11" width="36" height="9" rx="2" fill="#FFF" opacity=".9"/><rect x="12" y="26" width="30" height="3" rx="1.5" fill="#FFF"/>' },
IN: { h: ["#FF9933", "#FFF", "#138808"], e: '<circle cx="30" cy="20" r="4.5" fill="none" stroke="#000080" stroke-width="1.4"/><circle cx="30" cy="20" r="1" fill="#000080"/>' },
BR: { s: '<rect width="60" height="40" fill="#009739"/><path d="M30 5 55 20 30 35 5 20Z" fill="#FEDD00"/><circle cx="30" cy="20" r="8" fill="#012169"/><path d="M22.5 18.5c5-1.5 10-1 15 3" stroke="#FFF" stroke-width="1.6" fill="none"/>' },
MX: { v: ["#006341", "#FFF", "#CE1126"], e: '<circle cx="30" cy="20" r="4" fill="#8C6A2F"/>' },
AR: { h: ["#74ACDF", "#FFF", "#74ACDF"], e: '<circle cx="30" cy="20" r="4" fill="#F6B40E"/>' },
CL: { s: '<rect width="60" height="20" fill="#FFF"/><rect y="20" width="60" height="20" fill="#DA291C"/><rect width="20" height="20" fill="#0032A0"/>' + star5(10, 10, 5.5, "#FFF") },
KR: { s: '<rect width="60" height="40" fill="#FFF"/><circle cx="30" cy="20" r="9" fill="#CD2E3A"/><path d="M21 20a9 9 0 0 0 18 0 4.5 4.5 0 0 0-9 0 4.5 4.5 0 0 1-9 0Z" fill="#0047A0"/><path d="M9 9l6 4.5M8 11.5l6 4.5M45 26l6 4.5M44 28.5l6 4.5" stroke="#000" stroke-width="1.6"/>' },
TW: { s: '<rect width="60" height="40" fill="#FE0000"/><rect width="30" height="20" fill="#000095"/><circle cx="15" cy="10" r="6" fill="#FFF"/><circle cx="15" cy="10" r="3.5" fill="#000095"/><circle cx="15" cy="10" r="2.5" fill="#FFF"/>' },
TH: { h: [["#A51931", 1], ["#F4F5F8", 1], ["#2D2A4A", 2], ["#F4F5F8", 1], ["#A51931", 1]] },
VN: { s: '<rect width="60" height="40" fill="#DA251D"/>' + star5(30, 20, 9, "#FFCD00") },
ID: { h: ["#E70011", "#FFF"] },
MY: { h: ["#CC0001", "#FFF", "#CC0001", "#FFF", "#CC0001", "#FFF", "#CC0001"], e: '<rect width="30" height="22.9" fill="#010066"/><circle cx="11" cy="11" r="6" fill="#FFCC00"/><circle cx="13.5" cy="11" r="5.2" fill="#010066"/>' + star5(21, 11, 4, "#FFCC00") },
PH: { s: '<rect width="60" height="20" fill="#0038A8"/><rect y="20" width="60" height="20" fill="#CE1126"/><path d="M0 0 24 20 0 40Z" fill="#FFF"/><circle cx="8" cy="20" r="3.5" fill="#FCD116"/>' },
EG: { h: ["#CE1126", "#FFF", "#000"], e: '<circle cx="30" cy="20" r="3.5" fill="#C09300"/>' },
ZA: { s: '<rect width="60" height="16" fill="#E03C31"/><rect y="16" width="60" height="8" fill="#007749"/><rect y="24" width="60" height="16" fill="#001489"/><rect y="13" width="60" height="3" fill="#FFF"/><rect y="24" width="60" height="3" fill="#FFF"/><path d="M0 0 28 20 0 40Z" fill="#007749"/><path d="M0 5 21 20 0 35Z" fill="#FFB81C"/><path d="M0 10 14 20 0 30Z" fill="#000"/>' },
NG: { v: ["#008751", "#FFF", "#008751"] },
MA: { s: '<rect width="60" height="40" fill="#C1272D"/>' + star5(30, 20, 8, "#006233") }
};
// Собрать SVG флага по ISO2-коду; "" — если страна не поддержана.
function flagSVG(cc) {
const d = FLAGS[cc];
if (!d) return "";
let b = "";
const stripes = (arr, horiz) => {
const total = arr.reduce((s, x) => s + (Array.isArray(x) ? x[1] : 1), 0);
let pos = 0;
arr.forEach((x) => {
const col = Array.isArray(x) ? x[0] : x;
const len = ((Array.isArray(x) ? x[1] : 1) / total) * (horiz ? 40 : 60);
// +0.3 — перекрытие полос, чтобы не было волосяных зазоров при масштабировании.
b += horiz
? '<rect x="0" y="' + pos.toFixed(2) + '" width="60" height="' + (len + 0.3).toFixed(2) + '" fill="' + col + '"/>'
: '<rect x="' + pos.toFixed(2) + '" y="0" width="' + (len + 0.3).toFixed(2) + '" height="40" fill="' + col + '"/>';
pos += len;
});
};
if (d.s) b += d.s;
if (d.h) stripes(d.h, true);
if (d.v) stripes(d.v, false);
if (d.n) {
// Скандинавский крест, смещённый к древку.
const t = d.n[2] ? 12 : 8;
b += '<rect width="60" height="40" fill="' + d.n[0] + '"/>';
b += '<rect x="' + (22 - t / 2) + '" width="' + t + '" height="40" fill="' + d.n[1] + '"/><rect y="' + (20 - t / 2) + '" width="60" height="' + t + '" fill="' + d.n[1] + '"/>';
if (d.n[2]) b += '<rect x="19.5" width="5" height="40" fill="' + d.n[2] + '"/><rect y="17.5" width="60" height="5" fill="' + d.n[2] + '"/>';
}
if (d.e) b += d.e;
return '<svg class="fl" viewBox="0 0 60 40" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">' + b + '</svg>';
}
// ISO2 страны профиля: country от бэкенда, иначе распознаём эмодзи-флаг.
function profileISO(p) {
if (!p) return "";
const c = String(p.country || "").toUpperCase();
if (/^[A-Z]{2}$/.test(c)) return c;
const rs = Array.from(String(p.flag || ""));
if (rs.length === 2) {
const a = rs[0].codePointAt(0) - 0x1f1e6, b = rs[1].codePointAt(0) - 0x1f1e6;
if (a >= 0 && a < 26 && b >= 0 && b < 26) return String.fromCharCode(65 + a, 65 + b);
}
return "";
}
// Заполнить ячейку флага: SVG для известной страны, буквенный код — фолбэк.
function setFlagEl(el, p) {
const cc = profileISO(p);
const svg = flagSVG(cc);
el.classList.remove("unknown");
if (svg) {
el.innerHTML = svg;
} else {
el.textContent = cc || "·";
el.classList.add("unknown");
}
el.title = cc ? (countryRu(cc) || cc) : "регион неизвестен";
}
function pluralRu(n, one, few, many) {
const m10 = n % 10, m100 = n % 100;
@@ -1983,7 +2136,7 @@
function renderCurServer(activeName) {
const p = profiles.find((x) => x.name === (activeName || profile.value));
if (!p) {
$("curFlag").textContent = "·";
setFlagEl($("curFlag"), null);
$("curName").textContent = "Нет сервера";
$("curHost").textContent = "активируйте ключ на вкладке «Мои серверы»";
$("curProto").textContent = "—";
@@ -1991,7 +2144,7 @@
$("curPing").className = "ms";
return;
}
$("curFlag").textContent = p.flag || isoFlag(p.country) || "·";
setFlagEl($("curFlag"), p);
$("curName").textContent = friendlyTitle(p.name);
$("curHost").textContent = "нажмите, чтобы выбрать другой сервер";
$("curProto").textContent = (p.protocol || "?").toString();
@@ -2040,11 +2193,9 @@
const left = document.createElement("div");
left.className = "left";
const flagEmoji = p.flag || isoFlag(p.country);
const flag = document.createElement("span");
flag.className = "flag" + (flagEmoji ? "" : " unknown");
flag.textContent = flagEmoji || "·";
flag.title = p.country || "регион неизвестен";
flag.className = "flag";
setFlagEl(flag, p);
const metaCol = document.createElement("div");
metaCol.className = "meta-col";
const nameRow = document.createElement("div");
+1 -1
View File
@@ -18,7 +18,7 @@ import (
// CurrentVersion is the product/semver used for update eligibility (feed "version").
// Keep major.minor.patch only — no build suffix here.
const CurrentVersion = "4.1.1"
const CurrentVersion = "4.1.2"
// BuildNumber is the monotonic build within CurrentVersion (Windows FileVersion 4th part,
// macOS CFBundleVersion suffix, Android versionCode low digits). Bump on every release build.
+1 -1
View File
@@ -33,7 +33,7 @@
<Identity
Name="EvilFox.EvilFox"
Publisher="CN=EvilFox"
Version="4.1.1.1"
Version="4.1.2.1"
ProcessorArchitecture="x64" />
<Properties>
+2 -2
View File
@@ -15,7 +15,7 @@
param(
[string]$ExePath = "",
[string]$OutDir = "",
[string]$Version = "4.1.1.1",
[string]$Version = "4.1.2.1",
[string]$Name = "EvilFox.EvilFox",
[string]$Publisher = "CN=EvilFox",
[string]$DisplayName = "EvilFox",
@@ -82,7 +82,7 @@ $manifestText = Get-Content -LiteralPath $ManifestSrc -Raw -Encoding UTF8
# Prefer exact placeholder swaps so we never touch <?xml version=...?>.
$manifestText = $manifestText.Replace('Name="EvilFox.EvilFox"', "Name=`"$Name`"")
$manifestText = $manifestText.Replace('Publisher="CN=EvilFox"', "Publisher=`"$Publisher`"")
$manifestText = $manifestText.Replace('Version="4.1.1.1"', "Version=`"$Version`"")
$manifestText = $manifestText.Replace('Version="4.1.2.1"', "Version=`"$Version`"")
$manifestText = $manifestText.Replace('<DisplayName>EvilFox</DisplayName>', "<DisplayName>$DisplayName</DisplayName>")
$manifestText = $manifestText.Replace('<PublisherDisplayName>EvilFox</PublisherDisplayName>', "<PublisherDisplayName>$PublisherDisplayName</PublisherDisplayName>")
$manifestText = $manifestText.Replace('DisplayName="EvilFox"', "DisplayName=`"$DisplayName`"")
+4 -4
View File
@@ -1,16 +1,16 @@
{
"version": "4.1.1",
"notes": "4.1.1: исправлено отображение трафика, улучшены карточки провайдеров и список серверов",
"version": "4.1.2",
"notes": "4.1.2: настоящие флаги стран (SVG) — на Windows эмодзи-флаги не отображаются",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "362be966c08b95991deb3d7f47c839493fa1ef4cbc0f0325fb50f3d40b1f6225",
"sha256": "af18f836ec6607cc151ba373d2186548ba5d2033f4366445264285e92d370bee",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "362be966c08b95991deb3d7f47c839493fa1ef4cbc0f0325fb50f3d40b1f6225",
"sha256": "af18f836ec6607cc151ba373d2186548ba5d2033f4366445264285e92d370bee",
"os": "windows",
"arch": "amd64"
},
+4 -4
View File
@@ -1,7 +1,7 @@
{
"FixedFileInfo": {
"FileVersion": { "Major": 4, "Minor": 1, "Patch": 1, "Build": 1 },
"ProductVersion": { "Major": 4, "Minor": 1, "Patch": 1, "Build": 1 },
"FileVersion": { "Major": 4, "Minor": 1, "Patch": 2, "Build": 1 },
"ProductVersion": { "Major": 4, "Minor": 1, "Patch": 2, "Build": 1 },
"FileFlagsMask": "3f",
"FileFlags": "00",
"FileOS": "40004",
@@ -11,12 +11,12 @@
"StringFileInfo": {
"CompanyName": "EvilFox",
"FileDescription": "EvilFox — VPN client (Naive / Hy2 / AWG / VLESS / VMess / Trojan)",
"FileVersion": "4.1.1.1",
"FileVersion": "4.1.2.1",
"InternalName": "EvilFox",
"LegalCopyright": "Copyright (c) EvilFox",
"OriginalFilename": "EvilFox.exe",
"ProductName": "EvilFox",
"ProductVersion": "4.1.1.1",
"ProductVersion": "4.1.2.1",
"Comments": "Open-source VPN/proxy client. https://evilfox.win/"
},
"VarFileInfo": {