Release 4.2.1+1: UI polish — опечатки Румынии→RO+флаг, чистая шапка «Серверы» без +/×, «Полезные ссылки» (сайт+Telegram), убран мусор «СПЕЦИАЛЬНЫЕ СЕРВИСИ», sticky кнопка ПОДКЛЮЧИТЬСЯ, свойства сервера заполнены, без T/V в списке, оффер «3 дня бесплатного доступа». Windows 4.2.1.1.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Navis
2026-08-01 23:08:28 +03:00
co-authored by Cursor
parent 2a57987aea
commit 497af0e475
16 changed files with 311 additions and 317 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_020_001
versionName = "4.2.0+1"
versionCode = 4_021_001
versionName = "4.2.1+1"
vectorDrawables.useSupportLibrary = true
ndk {
abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64")
+46 -46
View File
@@ -1,48 +1,48 @@
{
"version": "4.2.0",
"notes": "4.2.0: обновлённые формулировки, Kill Switch, статистика, кнопка подключения",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "64da79ddf7cd5694bf93c4c110bf0e1281a20651e1e868a960562d5c1a15bd0d",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "64da79ddf7cd5694bf93c4c110bf0e1281a20651e1e868a960562d5c1a15bd0d",
"os": "windows",
"arch": "amd64"
},
"darwin-arm64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis",
"sha256": "01a40b1cce4e2fb3a38eb80f0815c295a00af11e4288f3b9e5c0b58ae336f28d",
"os": "darwin",
"arch": "arm64",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.app.zip"
},
"darwin-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis",
"sha256": "19a066cfc7f7c3a291a220482b28e66a459d091c19c5351818c401b982bc65b3",
"os": "darwin",
"arch": "amd64",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.app.zip"
},
"darwin-universal": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis",
"sha256": "d53dba8256fd6f7b62d1ce9bbad741af087ae44fec942bf2782b9c8f4e36cef2",
"os": "darwin",
"arch": "universal",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.app.zip"
},
"android": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/android/Navis.apk",
"sha256": "4416770bebe576eba42811990c4e4424f6722dea3a8513e6164dd34fc975b3cf",
"os": "android",
"arch": "universal"
}
}
"version": "4.2.1",
"notes": "4.2.1: исправлены опечатки Румынии (флаг RO), чистая шапка списка серверов, полезные ссылки вместо рекомендаций, убраны мусорные подписи на Защите, большая кнопка ПОДКЛЮЧИТЬСЯ всегда видна, свойства сервера заполнены, без T/V бейджей в списке",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "c3885d6d0c5b6e542fe1a172650c2f9480afde2b9016ceb1a5699f851761b837",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "c3885d6d0c5b6e542fe1a172650c2f9480afde2b9016ceb1a5699f851761b837",
"os": "windows",
"arch": "amd64"
},
"darwin-arm64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis",
"sha256": "01a40b1cce4e2fb3a38eb80f0815c295a00af11e4288f3b9e5c0b58ae336f28d",
"os": "darwin",
"arch": "arm64",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.app.zip"
},
"darwin-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis",
"sha256": "19a066cfc7f7c3a291a220482b28e66a459d091c19c5351818c401b982bc65b3",
"os": "darwin",
"arch": "amd64",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.app.zip"
},
"darwin-universal": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis",
"sha256": "d53dba8256fd6f7b62d1ce9bbad741af087ae44fec942bf2782b9c8f4e36cef2",
"os": "darwin",
"arch": "universal",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.app.zip"
},
"android": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/android/Navis.apk",
"sha256": "4416770bebe576eba42811990c4e4424f6722dea3a8513e6164dd34fc975b3cf",
"os": "android",
"arch": "universal"
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+46 -46
View File
@@ -1,48 +1,48 @@
{
"version": "4.2.0",
"notes": "4.2.0: обновлённые формулировки, Kill Switch, статистика, кнопка подключения",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "64da79ddf7cd5694bf93c4c110bf0e1281a20651e1e868a960562d5c1a15bd0d",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "64da79ddf7cd5694bf93c4c110bf0e1281a20651e1e868a960562d5c1a15bd0d",
"os": "windows",
"arch": "amd64"
},
"darwin-arm64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis",
"sha256": "01a40b1cce4e2fb3a38eb80f0815c295a00af11e4288f3b9e5c0b58ae336f28d",
"os": "darwin",
"arch": "arm64",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.app.zip"
},
"darwin-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis",
"sha256": "19a066cfc7f7c3a291a220482b28e66a459d091c19c5351818c401b982bc65b3",
"os": "darwin",
"arch": "amd64",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.app.zip"
},
"darwin-universal": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis",
"sha256": "d53dba8256fd6f7b62d1ce9bbad741af087ae44fec942bf2782b9c8f4e36cef2",
"os": "darwin",
"arch": "universal",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.app.zip"
},
"android": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/android/Navis.apk",
"sha256": "4416770bebe576eba42811990c4e4424f6722dea3a8513e6164dd34fc975b3cf",
"os": "android",
"arch": "universal"
}
}
"version": "4.2.1",
"notes": "4.2.1: исправлены опечатки Румынии (флаг RO), чистая шапка списка серверов, полезные ссылки вместо рекомендаций, убраны мусорные подписи на Защите, большая кнопка ПОДКЛЮЧИТЬСЯ всегда видна, свойства сервера заполнены, без T/V бейджей в списке",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "c3885d6d0c5b6e542fe1a172650c2f9480afde2b9016ceb1a5699f851761b837",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "c3885d6d0c5b6e542fe1a172650c2f9480afde2b9016ceb1a5699f851761b837",
"os": "windows",
"arch": "amd64"
},
"darwin-arm64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis",
"sha256": "01a40b1cce4e2fb3a38eb80f0815c295a00af11e4288f3b9e5c0b58ae336f28d",
"os": "darwin",
"arch": "arm64",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.app.zip"
},
"darwin-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis",
"sha256": "19a066cfc7f7c3a291a220482b28e66a459d091c19c5351818c401b982bc65b3",
"os": "darwin",
"arch": "amd64",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.app.zip"
},
"darwin-universal": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis",
"sha256": "d53dba8256fd6f7b62d1ce9bbad741af087ae44fec942bf2782b9c8f4e36cef2",
"os": "darwin",
"arch": "universal",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.app.zip"
},
"android": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/android/Navis.apk",
"sha256": "4416770bebe576eba42811990c4e4424f6722dea3a8513e6164dd34fc975b3cf",
"os": "android",
"arch": "universal"
}
}
}
+1 -3
View File
@@ -470,13 +470,11 @@ func (a *App) AutoCheckUpdate() {
}
// allowedLinkHosts are the only hosts the UI may open in the default browser
// (shop button, «Рекомендуемые сервисы» and provider cards in index.html).
// (shop / «Полезные ссылки» / provider cards in index.html).
// Competitor links are plain URLs without referral codes (Store policy).
var allowedLinkHosts = map[string]bool{
"evilfox.win": true,
"t.me": true,
"de4ima.uk": true,
"git.de4ima.uk": true,
"nordvpn.com": true,
"expressvpn.com": true,
"surfshark.com": true,
+153 -165
View File
@@ -273,8 +273,9 @@
overflow-y: auto;
padding: 18px 20px 8px;
}
.page { display: none; max-width: 640px; margin: 0 auto; animation: rise .3s var(--ease) both; }
.page.active { display: block; }
/* Inactive pages must not paint (WebView2 overflow ghosts from hidden siblings). */
.page { display: none !important; max-width: 640px; margin: 0 auto; }
.page.active { display: block !important; animation: rise .3s var(--ease) both; }
@keyframes rise {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: none; }
@@ -308,9 +309,12 @@
.update-actions .action { flex: 1 1 auto; min-width: 140px; }
/* ==== Home / power ==== */
/* Sticky so the big connect control never scrolls under the provider list. */
.hero {
position: relative;
overflow: hidden;
position: sticky;
top: 0;
z-index: 6;
overflow: visible;
border-radius: var(--radius);
border: 1px solid var(--line);
background: var(--hero-bg);
@@ -318,6 +322,8 @@
margin-bottom: 14px;
text-align: center;
transition: border-color .25s, box-shadow .25s, background .25s;
min-height: 220px;
box-sizing: border-box;
}
.hero.on {
border-color: rgba(13,138,102,.35);
@@ -327,6 +333,8 @@
.power-btn {
width: 148px;
height: 148px;
min-width: 148px;
min-height: 148px;
border-radius: 50%;
border: 0;
cursor: pointer;
@@ -341,6 +349,7 @@
transition: background .25s, color .25s, transform .12s, box-shadow .25s;
box-shadow: 0 14px 34px rgba(8, 145, 178, .3);
position: relative;
flex-shrink: 0;
}
.power-btn:hover { transform: translateY(-2px); }
.power-btn:active { transform: none; }
@@ -1336,7 +1345,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.2.0</span>
<span class="badge-ver" id="badgeVer" hidden></span>
</div>
<button class="nav-btn active" type="button" data-page="guard">
@@ -1362,7 +1371,7 @@
<div class="spacer"></div>
<div class="side-foot">
<span class="ver" id="verLabel">EvilFox</span>
<span class="ver" id="verLabel">EvilFox</span><!-- версия только в «Помощь» -->
<button type="button" class="theme-toggle" id="themeToggle" title="Тема" aria-label="Переключить тему">
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M20.5 14.2A8.2 8.2 0 0 1 9.8 3.5 8.5 8.5 0 1 0 20.5 14.2z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
@@ -1390,8 +1399,8 @@
<!-- ============ Защита ============ -->
<section class="page active" data-page="guard">
<section class="hero" id="hero">
<button class="power-btn" id="toggleBtn" type="button" aria-label="Подключить / отключить">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round">
<button class="power-btn" id="toggleBtn" type="button" aria-label="Подключиться или отключиться">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" aria-hidden="true">
<path d="M12 3v8"/>
<path d="M6.3 6.6a8 8 0 1 0 11.4 0"/>
</svg>
@@ -1404,7 +1413,7 @@
<div class="chips-line">
<span class="mode-chip" id="vpnChip" hidden>VPN · весь трафик</span>
</div>
<p class="hero-hint" id="heroHint">Нажмите, чтобы включить защиту</p>
<p class="hero-hint" id="heroHint">Нажмите большую кнопку, чтобы включить защиту</p>
</section>
<button class="cur-server" id="curServer" type="button" title="Открыть список серверов">
@@ -1428,37 +1437,33 @@
<div class="traffic-sub" id="trafficExp" hidden></div>
</section>
<div class="section">
<div class="section-title"><span>Выберите провайдера</span></div>
<div class="section" id="provSection">
<div class="section-title"><span>Провайдеры</span></div>
<div class="prov-grid" id="provGrid">
<div class="prov-card ours">
<span class="prov-badge">⭐ Рекомендуемый сервис</span>
<span class="prov-badge">⭐ Рекомендуемый</span>
<div class="prov-name">🦊 EvilFox VPN</div>
<div class="prov-sub">Оптимальные серверы рядом с вами · защита без настройки</div>
<span class="prov-gift" id="provGift">🚀 3 дня бесплатно</span>
<span class="prov-gift" id="provGift">🎁 3 дня бесплатного доступа</span>
<button class="action primary" id="provConnectBtn" type="button">🔥 Подключиться</button>
<button class="prov-ping" id="provPingOurs" type="button">📊 <span class="val">Проверить скорость</span></button>
</div>
<div class="prov-card">
<span class="prov-tag">Сторонний сервис</span>
<span class="prov-tag">Сторонний</span>
<div class="prov-name">NordVPN</div>
<div class="prov-sub">Оплата и настройка на сайте провайдера</div>
<button class="prov-go" type="button" data-url="https://nordvpn.com/" data-name="NordVPN">Перейти на сайт</button>
<button class="prov-ping" type="button" data-base="45">📊 <span class="val">Проверить скорость</span></button>
</div>
<div class="prov-card">
<span class="prov-tag">Сторонний сервис</span>
<span class="prov-tag">Сторонний</span>
<div class="prov-name">ExpressVPN</div>
<div class="prov-sub">Оплата и настройка на сайте провайдера</div>
<button class="prov-go" type="button" data-url="https://expressvpn.com/" data-name="ExpressVPN">Перейти на сайт</button>
<button class="prov-ping" type="button" data-base="38">📊 <span class="val">Проверить скорость</span></button>
</div>
<div class="prov-card">
<span class="prov-tag">Сторонний сервис</span>
<span class="prov-tag">Сторонний</span>
<div class="prov-name">Surfshark</div>
<div class="prov-sub">Оплата и настройка на сайте провайдера</div>
<button class="prov-go" type="button" data-url="https://surfshark.com/" data-name="Surfshark">Перейти на сайт</button>
<button class="prov-ping" type="button" data-base="55">📊 <span class="val">Проверить скорость</span></button>
</div>
</div>
</div>
@@ -1478,12 +1483,10 @@
</section>
<div class="server-toolbar">
<div class="section-title"><span>Серверы</span> <span class="count-note" id="srvCount"></span></div>
<div class="section-title"><span>🌍 Серверы</span> <span class="count-note" id="srvCount"></span></div>
<div class="server-actions">
<button class="mini" id="pingBtn" type="button">Пинг</button>
<button class="mini accent" id="bestBtn" type="button">Лучший</button>
<button class="icon-btn" id="addBtn" type="button" title="Новый профиль" style="width:34px;height:34px;font-size:1rem">+</button>
<button class="icon-btn danger" id="delBtn" type="button" title="Удалить" style="width:34px;height:34px;font-size:1rem">×</button>
<button class="mini accent" id="bestBtn" type="button" title="Выбрать сервер с наименьшим пингом">Выбрать лучший</button>
</div>
</div>
<div class="server-list" id="serverList"></div>
@@ -1507,18 +1510,16 @@
<details class="profile-edit" id="profileEdit">
<summary>Свойства сервера</summary>
<div class="stack" style="margin-top:8px">
<p class="host-line">Адрес сервера: <span id="profHost"></span></p>
<div>
<label class="field" for="name">Название</label>
<input id="name" type="text" placeholder="Например: DE-1 / Home" spellcheck="false" />
</div>
<div>
<label class="field" for="proxy">Ссылка / конфиг</label>
<textarea id="proxy" rows="3" placeholder="vless:// · vmess:// · trojan:// · hy2:// · naive+https:// · AWG .conf" spellcheck="false"></textarea>
</div>
<button class="action secondary" id="saveBtn" type="button">Сохранить профиль</button>
<div class="stack" style="margin-top:8px" id="profProps">
<div class="subinfo-row"><span class="k">Сервер</span><span class="v" id="propName"></span></div>
<div class="subinfo-row"><span class="k">Пинг</span><span class="v" id="propPing"></span></div>
<div class="subinfo-row"><span class="k">Протокол</span><span class="v" id="propProto"></span></div>
<div class="subinfo-row"><span class="k">Статус</span><span class="v" id="propStatus"></span></div>
</div>
<!-- Скрытые поля — нужны для connect/save, в UI не показываем. -->
<input id="name" type="text" hidden />
<textarea id="proxy" hidden rows="1"></textarea>
<button class="action secondary" id="saveBtn" type="button" hidden>Сохранить профиль</button>
</details>
</section>
@@ -1641,11 +1642,11 @@
<details class="panel" id="advBox">
<summary>Расширенные настройки</summary>
<div style="margin-top:6px">
<div style="margin-top:6px" id="proxyParamsBlock" hidden>
<div class="section-title" style="margin-bottom:2px"><span>Параметры прокси</span></div>
<p class="adv-line">HTTP: <span id="advHttp"></span></p>
<p class="adv-line">SOCKS: <span id="advSocks"></span></p>
<p class="remna-hint" style="margin-top:8px">Локальные адреса прокси на этом компьютере — нужны только для ручной настройки отдельных программ. Обычным пользователям менять ничего не требуется.</p>
<p class="remna-hint" style="margin-top:8px">Локальные адреса прокси на этом компьютере — нужны только для ручной настройки отдельных программ.</p>
</div>
</details>
</section>
@@ -1707,7 +1708,7 @@
</details>
<details class="panel">
<summary>Не подключается — что делать?</summary>
<p class="remna-hint" style="margin-top:6px">Проверьте, что конфигурация добавлена и срок её действия не истёк («Мои серверы» → «Информация об аккаунте»). Попробуйте другой сервер (кнопка «Лучший»). Подробности — в журнале на вкладке «Статистика».</p>
<p class="remna-hint" style="margin-top:6px">Проверьте, что конфигурация добавлена и срок её действия не истёк («Мои серверы» → «Информация об аккаунте»). Попробуйте другой сервер (кнопка «Выбрать лучший»). Подробности — в журнале на вкладке «Статистика».</p>
</details>
<div class="about-card">
@@ -1725,10 +1726,9 @@
<button class="action primary" id="shopBtn" type="button">Перейти на сайт</button>
<button class="shop-link" id="shopLink" type="button">https://evilfox.win/</button>
</section>
<!-- Рекомендуемые сервисы: ненавязчивый блок в нижнем углу «О программе».
Ссылки задаются в скрипте — константа RECOMMENDED_SERVICES. -->
<section class="svc" aria-label="Рекомендуемые сервисы">
<h4>Рекомендуемые сервисы</h4>
<!-- Полезные ссылки (сайт + поддержка). Хосты — в allowlist openURL. -->
<section class="svc" aria-label="Полезные ссылки">
<h4>🌐 Полезные ссылки</h4>
<div class="svc-grid" id="svcGrid"></div>
</section>
<button class="shop-link" id="quitBtn" type="button" hidden style="display:none;margin:12px auto 0;width:auto">Выйти</button>
@@ -1791,8 +1791,8 @@
const btn = $("toggleBtn");
const coreBtn = $("coreBtn");
const saveBtn = $("saveBtn");
const addBtn = $("addBtn");
const delBtn = $("delBtn");
const addBtn = $("addBtn"); // optional — header + removed
const delBtn = $("delBtn"); // optional — header × removed
const proxy = $("proxy");
const nameInput = $("name");
const sysproxy = $("sysproxy");
@@ -1843,10 +1843,30 @@
PT: "Португалия", RO: "Румыния", RS: "Сербия", RU: "Россия", SE: "Швеция",
SG: "Сингапур", SK: "Словакия", TR: "Турция", UA: "Украина", US: "США", ZA: "ЮАР"
};
// Опечатки / варианты имён стран → ISO (зеркало Go geoflag; длинные сначала).
const COUNTRY_NAME_ALIASES = [
["румьиния", "RO"], ["румяння", "RO"], ["румьиня", "RO"],
["румыния", "RO"], ["румыня", "RO"], ["руминия", "RO"], ["румыниа", "RO"],
["romania", "RO"], ["bucharest", "RO"], ["бухарест", "RO"]
];
function countryRu(cc) {
const c = String(cc || "").toUpperCase();
return COUNTRY_RU[c] || "";
}
function isoFromNameText(text) {
const lower = String(text || "").toLowerCase();
if (!lower) return "";
for (let i = 0; i < COUNTRY_NAME_ALIASES.length; i++) {
if (lower.indexOf(COUNTRY_NAME_ALIASES[i][0]) >= 0) return COUNTRY_NAME_ALIASES[i][1];
}
// Прямое совпадение канонического русского названия.
const keys = Object.keys(COUNTRY_RU);
for (let i = 0; i < keys.length; i++) {
const ru = COUNTRY_RU[keys[i]].toLowerCase();
if (ru && lower.indexOf(ru) >= 0) return keys[i];
}
return "";
}
// ==== SVG-флаги ====
// Windows не умеет эмодзи-флаги: Segoe UI Emoji рисует пары региональных
// индикаторов буквами («BE», «LV»), поэтому рисуем настоящие SVG-флаги.
@@ -1976,7 +1996,7 @@
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 от бэкенда, иначе распознаём эмодзи-флаг.
// ISO2 страны профиля: country от бэкенда, иначе имя/флаг, иначе алиасы опечаток.
function profileISO(p) {
if (!p) return "";
const c = String(p.country || "").toUpperCase();
@@ -1986,7 +2006,7 @@
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 "";
return isoFromNameText(p.name) || isoFromNameText(p.host) || "";
}
// Заполнить ячейку флага: SVG для известной страны, буквенный код — фолбэк.
function setFlagEl(el, p) {
@@ -2084,15 +2104,12 @@
const SHOP_URL = "https://evilfox.win/";
// Текст оффера — правьте здесь (показывается на карточке EvilFox на вкладке «Защита»).
// Правила Store: никаких цен и упоминаний оплаты внутри приложения.
const OFFER_TEXT = "🚀 3 дня бесплатно";
// Список рекомендуемых сервисов — замените ссылки/названия здесь.
// Хост каждой ссылки должен быть разрешён в allowlist openURL
// (internal/apphost/app.go → allowedLinkHosts), иначе ссылка не откроется.
const OFFER_TEXT = "🎁 3 дня бесплатного доступа";
// Полезные ссылки (Помощь). Хост каждой ссылки — в allowlist openURL
// (internal/apphost/app.go → allowedLinkHosts).
const RECOMMENDED_SERVICES = [
{ icon: "🛒", name: "EvilFox Shop", url: "https://evilfox.win/" },
{ icon: "✈️", name: "Telegram", url: "https://t.me/evilfox" },
{ icon: "🖥️", name: "Хостинг de4ima", url: "https://de4ima.uk" },
{ icon: "🦊", name: "Git", url: "https://git.de4ima.uk/Evilfox/navi" }
{ icon: "🌐", name: "Сайт", url: "https://evilfox.win/" },
{ icon: "✈️", name: "Поддержка: Telegram", url: "https://t.me/evilfox" }
];
const AUTO_BEST_KEY = "navis.autoBest";
// Оффер на карточке EvilFox рендерим из константы, чтобы правка была в одном месте.
@@ -2268,7 +2285,7 @@
async function persistHy2IfNeeded(protocol) {
const p = (protocol || "").toLowerCase();
const link = proxy.value.trim().toLowerCase();
const link = ((proxy && proxy.value) || "").trim().toLowerCase();
if (p === "hysteria2" || link.startsWith("hysteria2://") || link.startsWith("hy2://")) {
await saveHy2(readHy2());
}
@@ -2334,12 +2351,13 @@
// Человекочитаемые имена серверов («Чехия — оптимальный сервер», «Германия — сервер 2»).
// Реальный host:port показываем только в свойствах сервера и в журнале.
// ISO всегда через profileISO (бэкенд + JS-алиасы опечаток) → countryRu, никогда сырое имя панели.
let friendlyMap = {};
function rebuildFriendlyNames() {
friendlyMap = {};
const groups = {};
profiles.forEach((p) => {
const key = String(p.country || "").toUpperCase() || "__none__";
const key = profileISO(p) || "__none__";
(groups[key] = groups[key] || []).push(p);
});
Object.keys(groups).forEach((key) => {
@@ -2360,13 +2378,19 @@
} else {
title = region ? (region + " — сервер " + n) : ("Сервер " + n);
}
friendlyMap[p.name] = { title: title, region: region };
friendlyMap[p.name] = { title: title, region: region, iso: key === "__none__" ? "" : key };
});
});
}
function friendlyTitle(name) {
const f = friendlyMap[name];
return (f && f.title) || name || "";
if (f && f.title) return f.title;
// Фолбэк: не показываем сырые опечатки панели — только каноническое имя по ISO.
const p = profiles.find((x) => x.name === name);
const iso = profileISO(p) || isoFromNameText(name);
const region = countryRu(iso);
if (region) return region;
return "Сервер";
}
function fillProfiles(list, active) {
@@ -2481,10 +2505,7 @@
const right = document.createElement("div");
right.className = "right";
// Протокол — крошечная буква-иконка с подсказкой; строчный «vless»
// под каждой строкой не повторяем.
const protoBadge = protoLetterBadge(p.protocol);
if (protoBadge) right.appendChild(protoBadge);
// Протокол — только на карточке активного сервера / в «Свойствах», не в строках списка.
const ms = document.createElement("span");
const pr = pingMap[p.name];
if (pr && pr.ok) {
@@ -2500,12 +2521,12 @@
ms.title = "пинг не измерен";
}
right.appendChild(ms);
// [📊] — проверить скорость только этого сервера.
// [📊] — проверить пинг только этого сервера.
const pingOne = document.createElement("button");
pingOne.type = "button";
pingOne.className = "row-ping";
pingOne.textContent = "📊";
pingOne.title = "Проверить скорость этого сервера";
pingOne.title = "Проверить пинг этого сервера";
pingOne.addEventListener("click", (e) => {
e.stopPropagation();
runPingOne(p.name, pingOne, ms);
@@ -2520,20 +2541,6 @@
});
}
const PROTO_LETTER = {
vless: "V", vmess: "V", trojan: "T", hysteria2: "H", hy2: "H",
naive: "N", awg: "A"
};
function protoLetterBadge(protocol) {
const p = String(protocol || "").toLowerCase();
if (!p) return null;
const el = document.createElement("span");
el.className = "proto-badge";
el.textContent = PROTO_LETTER[p] || p.charAt(0).toUpperCase();
el.title = "Протокол: " + p;
return el;
}
// Пинг одного сервера по кнопке [📊] в строке.
async function runPingOne(name, btnEl, msEl) {
if (btnEl.disabled) return;
@@ -2689,11 +2696,17 @@
const daysLeft = Math.floor((d.getTime() - Date.now()) / 86400000);
const v = $("subExpireVal");
if (daysLeft < 0) {
v.textContent = text + " · срок истёк";
v.textContent = text + " · Истёк";
v.classList.add("warn");
} else {
v.textContent = daysLeft <= 14 ? (text + " · осталось " + daysLeft + " дн.") : text;
} else if (daysLeft === 0) {
v.textContent = text + " · Сегодня";
v.classList.add("warn");
} else if (daysLeft <= 14) {
v.textContent = text + " · осталось " + daysLeft + " дн.";
v.classList.toggle("warn", daysLeft <= 3);
} else {
v.textContent = text;
v.classList.remove("warn");
}
}
@@ -2713,12 +2726,12 @@
}
function renderUpdate(u, version) {
const label = "EvilFox v" + (version || "?");
verLabel.textContent = label;
// Версия — только в «Помощь»; в сайдбаре бренд без номера.
if (verLabel) verLabel.textContent = "EvilFox";
if (aboutVer) aboutVer.textContent = "Версия " + (version || "?");
if (badgeVer && version) {
const short = String(version).split("+")[0];
badgeVer.textContent = short || version;
if (badgeVer) {
badgeVer.hidden = true;
badgeVer.textContent = "";
}
if (storePackaged || (u && u.store_managed)) {
updateBanner.classList.remove("show");
@@ -2780,14 +2793,14 @@
}
const lock = connected || busy;
proxy.disabled = lock;
nameInput.disabled = lock;
if (proxy) proxy.disabled = lock;
if (nameInput) nameInput.disabled = lock;
sysproxy.disabled = lock;
profile.disabled = lock;
addBtn.disabled = lock;
delBtn.disabled = lock || ((state.profiles || profiles).length <= 1);
saveBtn.disabled = lock;
coreBtn.disabled = busy;
if (addBtn) addBtn.disabled = lock;
if (delBtn) delBtn.disabled = lock || ((state.profiles || profiles).length <= 1);
if (saveBtn) saveBtn.disabled = lock;
if (coreBtn) coreBtn.disabled = busy;
pingBtn.disabled = busy;
bestBtn.disabled = busy;
updCheckBtn.disabled = busy;
@@ -2808,8 +2821,8 @@
if (syncForm || (!formHydrated && !dirty)) {
const active = (state.profiles || []).find((p) => p.name === profile.value) || {};
nameInput.value = active.name || state.active_profile || "";
proxy.value = typeof state.proxy === "string" ? state.proxy : (active.proxy || "");
if (nameInput) nameInput.value = active.name || state.active_profile || "";
if (proxy) proxy.value = typeof state.proxy === "string" ? state.proxy : (active.proxy || "");
if (typeof state.system_proxy === "boolean") sysproxy.checked = state.system_proxy;
fillHy2(state.hy2);
formHydrated = true;
@@ -2826,10 +2839,13 @@
const hy2 = (label === "hysteria2" || label === "hy2");
if ($("hy2Box")) $("hy2Box").style.display = hy2 ? "" : "none";
// Технические адреса прокси — только в «Настройки → Расширенные» и на «Статистике».
// Параметры прокси — только когда подключены (без пустого «появится после…»).
const advHttp = $("advHttp"), advSocks = $("advSocks"), logsProxyLine = $("logsProxyLine");
if (advHttp) advHttp.textContent = state.http_proxy || "появится после подключения";
if (advSocks) advSocks.textContent = state.socks_proxy || "появится после подключения";
const proxyParamsBlock = $("proxyParamsBlock");
const hasProxyParams = !!(state.http_proxy || state.socks_proxy);
if (proxyParamsBlock) proxyParamsBlock.hidden = !hasProxyParams;
if (advHttp) advHttp.textContent = state.http_proxy || "—";
if (advSocks) advSocks.textContent = state.socks_proxy || "—";
if (logsProxyLine) {
const parts = [];
if (state.http_proxy) parts.push("HTTP " + state.http_proxy);
@@ -2838,7 +2854,7 @@
logsProxyLine.textContent = parts.length ? ("Прокси: " + parts.join(" · ")) : "";
}
renderProvConnect();
renderProfHost();
renderProfProps();
// Нижняя полоса статуса — только для важных сообщений; постоянного
// «Готово к подключению» больше нет (статус виден на большой кнопке).
@@ -2878,12 +2894,30 @@
}
}
// Реальный host:port выбранного сервера — в «Свойствах сервера» (муторные детали).
function renderProfHost() {
const el = $("profHost");
if (!el) return;
// Свойства выбранного сервера: имя / пинг / протокол / статус.
function renderProfProps() {
const p = profiles.find((x) => x.name === profile.value);
el.textContent = (p && p.host) ? p.host : "—";
const nameEl = $("propName"), pingEl = $("propPing"), protoEl = $("propProto"), stEl = $("propStatus");
if (!nameEl) return;
if (!p) {
nameEl.textContent = "—";
if (pingEl) pingEl.textContent = "—";
if (protoEl) protoEl.textContent = "—";
if (stEl) stEl.textContent = "—";
return;
}
nameEl.textContent = friendlyTitle(p.name);
const pr = pingMap[p.name];
if (pingEl) {
if (pr && pr.ok) pingEl.textContent = pr.ms + " ms";
else if (pr && pr.error) pingEl.textContent = "недоступен";
else pingEl.textContent = "не измерен";
}
if (protoEl) {
const proto = String(p.protocol || "—");
protoEl.textContent = proto.charAt(0).toUpperCase() + proto.slice(1);
}
if (stEl) stEl.textContent = connected ? "🟢 Активен" : "⚪ Не активен";
}
async function refresh(opts) {
@@ -2949,23 +2983,31 @@
await runBest(true);
} else {
setMeta("Подключение…");
await saveProfile(nameInput.value.trim() || profile.value, proxy.value.trim(), !!sysproxy.checked);
await persistHy2IfNeeded();
// Профиль уже выбран; скрытые поля name/proxy гидратируются из state.
// Не перезаписываем proxy пустой строкой, если форма ещё не заполнена.
const n = (nameInput && nameInput.value.trim()) || profile.value;
const px = (proxy && proxy.value.trim()) || "";
if (n && px) {
await saveProfile(n, px, !!sysproxy.checked);
await persistHy2IfNeeded();
}
await connect();
setMeta("Защищённое соединение установлено", "ok");
}
} catch (e) { setMeta(String(e), "err"); }
}));
saveBtn.addEventListener("click", () => withBusy(async () => {
if (saveBtn) saveBtn.addEventListener("click", () => withBusy(async () => {
try {
await saveProfile(nameInput.value.trim() || profile.value, proxy.value.trim(), !!sysproxy.checked);
const n = (nameInput && nameInput.value.trim()) || profile.value;
const px = (proxy && proxy.value.trim()) || "";
await saveProfile(n, px, !!sysproxy.checked);
await persistHy2IfNeeded();
setMeta("Профиль сохранён", "ok");
} catch (e) { setMeta(String(e), "err"); }
}));
coreBtn.addEventListener("click", () => withBusy(async () => {
if (coreBtn) coreBtn.addEventListener("click", () => withBusy(async () => {
try {
setMeta("Скачивание official cores…");
const path = await installCore();
@@ -2973,7 +3015,7 @@
} catch (e) { setMeta(String(e), "err"); }
}));
delBtn.addEventListener("click", () => withBusy(async () => {
if (delBtn) delBtn.addEventListener("click", () => withBusy(async () => {
try {
if (!confirm("Удалить сервер «" + friendlyTitle(profile.value) + "»?")) return;
await deleteProfile(profile.value);
@@ -2983,7 +3025,7 @@
} catch (e) { setMeta(String(e), "err"); }
}));
addBtn.addEventListener("click", () => {
if (addBtn) addBtn.addEventListener("click", () => {
$("newName").value = "";
$("newProxy").value = "";
modal.classList.add("open");
@@ -3022,7 +3064,7 @@
shopBtn.addEventListener("click", openShop);
shopLink.addEventListener("click", openShop);
// «Рекомендуемые сервисы» — рендер из RECOMMENDED_SERVICES (см. константу выше).
// «Рекомендуемые сервисы» → «Полезные ссылки» (см. RECOMMENDED_SERVICES).
(function renderRecommendedServices() {
const grid = $("svcGrid");
if (!grid) return;
@@ -3097,60 +3139,6 @@
if (!connected && !busy) btn.click();
});
function setPingBtnResult(b, ms) {
const val = b.querySelector(".val");
if (val) val.textContent = ms + " мс";
b.classList.toggle("good", ms < 40);
b.classList.toggle("mid", ms >= 40);
}
// «Проверить скорость» на карточке EvilFox — реальный пинг до наших серверов.
const provPingOurs = $("provPingOurs");
if (provPingOurs) provPingOurs.addEventListener("click", async () => {
if (provPingOurs.disabled) return;
const val = provPingOurs.querySelector(".val");
provPingOurs.disabled = true;
if (val) val.textContent = "измерение…";
try {
const rows = await pingServers();
rememberPings(rows);
renderServerList(profile.value);
renderCurServer(profile.value);
const oks = (rows || []).filter((r) => r.ok).map((r) => r.ms);
if (oks.length) {
setPingBtnResult(provPingOurs, Math.min.apply(null, oks));
} else if (val) {
val.textContent = "серверы недоступны";
}
} catch (_) {
if (val) val.textContent = "не удалось измерить";
} finally {
provPingOurs.disabled = false;
}
});
// «Проверить скорость» у сторонних провайдеров — оценка задержки до их
// публичных серверов от текущего местоположения (ориентировочно).
document.querySelectorAll(".prov-ping[data-base]").forEach((b) => {
b.addEventListener("click", () => {
if (b.disabled) return;
const val = b.querySelector(".val");
b.disabled = true;
if (val) val.textContent = "измерение…";
const base = parseInt(b.dataset.base, 10) || 45;
setTimeout(() => {
let ms = base + Math.round(Math.random() * 12 - 5);
const ourBest = Object.keys(pingMap)
.map((k) => pingMap[k])
.filter((p) => p && p.ok)
.reduce((m, p) => Math.min(m, p.ms), Infinity);
if (isFinite(ourBest) && ms <= ourBest + 15) ms = ourBest + 18 + Math.round(Math.random() * 10);
setPingBtnResult(b, ms);
b.disabled = false;
}, 1500 + Math.random() * 900);
});
});
subBtn.addEventListener("click", () => withBusy(async () => {
try {
await runImportSubscription();
+3 -1
View File
@@ -141,7 +141,9 @@ var countryNames = []nameCode{
{"сша", "US"}, {"америка", "US"},
// Частые опечатки в именах серверов на панелях (наблюдались в проде).
{"румьиня", "RO"}, {"румыня", "RO"}, {"руминия", "RO"},
// Longer stems first (Contains): «румьиния» before «румьиня».
{"румьиния", "RO"}, {"румяння", "RO"}, {"румьиня", "RO"},
{"румыня", "RO"}, {"руминия", "RO"}, {"румыниа", "RO"},
// City → country hints (EN + RU).
{"frankfurt", "DE"}, {"amsterdam", "NL"}, {"london", "GB"},
+6
View File
@@ -28,6 +28,12 @@ func TestFromText(t *testing.T) {
// Panel-side typos seen in production.
{"Румьиня", "RO"},
{"Румыня-2", "RO"},
{"Румяння", "RO"},
{"Румяння — оптимальный", "RO"},
{"Румьиния", "RO"},
{"Румьиния Fast", "RO"},
{"румыниа", "RO"},
{"Румыния", "RO"},
}
for _, c := range cases {
cc, emoji := FromText(c.in)
+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.2.0"
const CurrentVersion = "4.2.1"
// 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.2.0.1"
Version="4.2.1.1"
ProcessorArchitecture="x64" />
<Properties>
+2 -2
View File
@@ -15,7 +15,7 @@
param(
[string]$ExePath = "",
[string]$OutDir = "",
[string]$Version = "4.2.0.1",
[string]$Version = "4.2.1.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.2.0.1"', "Version=`"$Version`"")
$manifestText = $manifestText.Replace('Version="4.2.1.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`"")
+46 -46
View File
@@ -1,48 +1,48 @@
{
"version": "4.2.0",
"notes": "4.2.0: обновлённые формулировки, Kill Switch, статистика, кнопка подключения",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "64da79ddf7cd5694bf93c4c110bf0e1281a20651e1e868a960562d5c1a15bd0d",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "64da79ddf7cd5694bf93c4c110bf0e1281a20651e1e868a960562d5c1a15bd0d",
"os": "windows",
"arch": "amd64"
},
"darwin-arm64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis",
"sha256": "01a40b1cce4e2fb3a38eb80f0815c295a00af11e4288f3b9e5c0b58ae336f28d",
"os": "darwin",
"arch": "arm64",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.app.zip"
},
"darwin-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis",
"sha256": "19a066cfc7f7c3a291a220482b28e66a459d091c19c5351818c401b982bc65b3",
"os": "darwin",
"arch": "amd64",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.app.zip"
},
"darwin-universal": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis",
"sha256": "d53dba8256fd6f7b62d1ce9bbad741af087ae44fec942bf2782b9c8f4e36cef2",
"os": "darwin",
"arch": "universal",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.app.zip"
},
"android": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/android/Navis.apk",
"sha256": "4416770bebe576eba42811990c4e4424f6722dea3a8513e6164dd34fc975b3cf",
"os": "android",
"arch": "universal"
}
}
"version": "4.2.1",
"notes": "4.2.1: исправлены опечатки Румынии (флаг RO), чистая шапка списка серверов, полезные ссылки вместо рекомендаций, убраны мусорные подписи на Защите, большая кнопка ПОДКЛЮЧИТЬСЯ всегда видна, свойства сервера заполнены, без T/V бейджей в списке",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "c3885d6d0c5b6e542fe1a172650c2f9480afde2b9016ceb1a5699f851761b837",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "c3885d6d0c5b6e542fe1a172650c2f9480afde2b9016ceb1a5699f851761b837",
"os": "windows",
"arch": "amd64"
},
"darwin-arm64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis",
"sha256": "01a40b1cce4e2fb3a38eb80f0815c295a00af11e4288f3b9e5c0b58ae336f28d",
"os": "darwin",
"arch": "arm64",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.app.zip"
},
"darwin-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis",
"sha256": "19a066cfc7f7c3a291a220482b28e66a459d091c19c5351818c401b982bc65b3",
"os": "darwin",
"arch": "amd64",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.app.zip"
},
"darwin-universal": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis",
"sha256": "d53dba8256fd6f7b62d1ce9bbad741af087ae44fec942bf2782b9c8f4e36cef2",
"os": "darwin",
"arch": "universal",
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.dmg",
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.app.zip"
},
"android": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/android/Navis.apk",
"sha256": "4416770bebe576eba42811990c4e4424f6722dea3a8513e6164dd34fc975b3cf",
"os": "android",
"arch": "universal"
}
}
}
+4 -4
View File
@@ -1,7 +1,7 @@
{
"FixedFileInfo": {
"FileVersion": { "Major": 4, "Minor": 2, "Patch": 0, "Build": 1 },
"ProductVersion": { "Major": 4, "Minor": 2, "Patch": 0, "Build": 1 },
"FileVersion": { "Major": 4, "Minor": 2, "Patch": 1, "Build": 1 },
"ProductVersion": { "Major": 4, "Minor": 2, "Patch": 1, "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.2.0.1",
"FileVersion": "4.2.1.1",
"InternalName": "EvilFox",
"LegalCopyright": "Copyright (c) EvilFox",
"OriginalFilename": "EvilFox.exe",
"ProductName": "EvilFox",
"ProductVersion": "4.2.0.1",
"ProductVersion": "4.2.1.1",
"Comments": "Open-source VPN/proxy client. https://evilfox.win/"
},
"VarFileInfo": {