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:
@@ -13,8 +13,8 @@ android {
|
|||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
// versionCode = major*1_000_000 + minor*10_000 + patch*100 + build
|
// versionCode = major*1_000_000 + minor*10_000 + patch*100 + build
|
||||||
versionCode = 4_020_001
|
versionCode = 4_021_001
|
||||||
versionName = "4.2.0+1"
|
versionName = "4.2.1+1"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64")
|
abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64")
|
||||||
|
|||||||
Vendored
+46
-46
@@ -1,48 +1,48 @@
|
|||||||
{
|
{
|
||||||
"version": "4.2.0",
|
"version": "4.2.1",
|
||||||
"notes": "4.2.0: обновлённые формулировки, Kill Switch, статистика, кнопка подключения",
|
"notes": "4.2.1: исправлены опечатки Румынии (флаг RO), чистая шапка списка серверов, полезные ссылки вместо рекомендаций, убраны мусорные подписи на Защите, большая кнопка ПОДКЛЮЧИТЬСЯ всегда видна, свойства сервера заполнены, без T/V бейджей в списке",
|
||||||
"platform": "windows-amd64",
|
"platform": "windows-amd64",
|
||||||
"os": "windows",
|
"os": "windows",
|
||||||
"arch": "amd64",
|
"arch": "amd64",
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
|
||||||
"sha256": "64da79ddf7cd5694bf93c4c110bf0e1281a20651e1e868a960562d5c1a15bd0d",
|
"sha256": "c3885d6d0c5b6e542fe1a172650c2f9480afde2b9016ceb1a5699f851761b837",
|
||||||
"mandatory": false,
|
"mandatory": false,
|
||||||
"platforms": {
|
"platforms": {
|
||||||
"windows-amd64": {
|
"windows-amd64": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
|
||||||
"sha256": "64da79ddf7cd5694bf93c4c110bf0e1281a20651e1e868a960562d5c1a15bd0d",
|
"sha256": "c3885d6d0c5b6e542fe1a172650c2f9480afde2b9016ceb1a5699f851761b837",
|
||||||
"os": "windows",
|
"os": "windows",
|
||||||
"arch": "amd64"
|
"arch": "amd64"
|
||||||
},
|
},
|
||||||
"darwin-arm64": {
|
"darwin-arm64": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis",
|
||||||
"sha256": "01a40b1cce4e2fb3a38eb80f0815c295a00af11e4288f3b9e5c0b58ae336f28d",
|
"sha256": "01a40b1cce4e2fb3a38eb80f0815c295a00af11e4288f3b9e5c0b58ae336f28d",
|
||||||
"os": "darwin",
|
"os": "darwin",
|
||||||
"arch": "arm64",
|
"arch": "arm64",
|
||||||
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.dmg",
|
"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"
|
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.app.zip"
|
||||||
},
|
},
|
||||||
"darwin-amd64": {
|
"darwin-amd64": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis",
|
||||||
"sha256": "19a066cfc7f7c3a291a220482b28e66a459d091c19c5351818c401b982bc65b3",
|
"sha256": "19a066cfc7f7c3a291a220482b28e66a459d091c19c5351818c401b982bc65b3",
|
||||||
"os": "darwin",
|
"os": "darwin",
|
||||||
"arch": "amd64",
|
"arch": "amd64",
|
||||||
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.dmg",
|
"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"
|
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.app.zip"
|
||||||
},
|
},
|
||||||
"darwin-universal": {
|
"darwin-universal": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis",
|
||||||
"sha256": "d53dba8256fd6f7b62d1ce9bbad741af087ae44fec942bf2782b9c8f4e36cef2",
|
"sha256": "d53dba8256fd6f7b62d1ce9bbad741af087ae44fec942bf2782b9c8f4e36cef2",
|
||||||
"os": "darwin",
|
"os": "darwin",
|
||||||
"arch": "universal",
|
"arch": "universal",
|
||||||
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.dmg",
|
"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"
|
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.app.zip"
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/android/Navis.apk",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/android/Navis.apk",
|
||||||
"sha256": "4416770bebe576eba42811990c4e4424f6722dea3a8513e6164dd34fc975b3cf",
|
"sha256": "4416770bebe576eba42811990c4e4424f6722dea3a8513e6164dd34fc975b3cf",
|
||||||
"os": "android",
|
"os": "android",
|
||||||
"arch": "universal"
|
"arch": "universal"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
+46
-46
@@ -1,48 +1,48 @@
|
|||||||
{
|
{
|
||||||
"version": "4.2.0",
|
"version": "4.2.1",
|
||||||
"notes": "4.2.0: обновлённые формулировки, Kill Switch, статистика, кнопка подключения",
|
"notes": "4.2.1: исправлены опечатки Румынии (флаг RO), чистая шапка списка серверов, полезные ссылки вместо рекомендаций, убраны мусорные подписи на Защите, большая кнопка ПОДКЛЮЧИТЬСЯ всегда видна, свойства сервера заполнены, без T/V бейджей в списке",
|
||||||
"platform": "windows-amd64",
|
"platform": "windows-amd64",
|
||||||
"os": "windows",
|
"os": "windows",
|
||||||
"arch": "amd64",
|
"arch": "amd64",
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
|
||||||
"sha256": "64da79ddf7cd5694bf93c4c110bf0e1281a20651e1e868a960562d5c1a15bd0d",
|
"sha256": "c3885d6d0c5b6e542fe1a172650c2f9480afde2b9016ceb1a5699f851761b837",
|
||||||
"mandatory": false,
|
"mandatory": false,
|
||||||
"platforms": {
|
"platforms": {
|
||||||
"windows-amd64": {
|
"windows-amd64": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
|
||||||
"sha256": "64da79ddf7cd5694bf93c4c110bf0e1281a20651e1e868a960562d5c1a15bd0d",
|
"sha256": "c3885d6d0c5b6e542fe1a172650c2f9480afde2b9016ceb1a5699f851761b837",
|
||||||
"os": "windows",
|
"os": "windows",
|
||||||
"arch": "amd64"
|
"arch": "amd64"
|
||||||
},
|
},
|
||||||
"darwin-arm64": {
|
"darwin-arm64": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis",
|
||||||
"sha256": "01a40b1cce4e2fb3a38eb80f0815c295a00af11e4288f3b9e5c0b58ae336f28d",
|
"sha256": "01a40b1cce4e2fb3a38eb80f0815c295a00af11e4288f3b9e5c0b58ae336f28d",
|
||||||
"os": "darwin",
|
"os": "darwin",
|
||||||
"arch": "arm64",
|
"arch": "arm64",
|
||||||
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.dmg",
|
"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"
|
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.app.zip"
|
||||||
},
|
},
|
||||||
"darwin-amd64": {
|
"darwin-amd64": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis",
|
||||||
"sha256": "19a066cfc7f7c3a291a220482b28e66a459d091c19c5351818c401b982bc65b3",
|
"sha256": "19a066cfc7f7c3a291a220482b28e66a459d091c19c5351818c401b982bc65b3",
|
||||||
"os": "darwin",
|
"os": "darwin",
|
||||||
"arch": "amd64",
|
"arch": "amd64",
|
||||||
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.dmg",
|
"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"
|
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.app.zip"
|
||||||
},
|
},
|
||||||
"darwin-universal": {
|
"darwin-universal": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis",
|
||||||
"sha256": "d53dba8256fd6f7b62d1ce9bbad741af087ae44fec942bf2782b9c8f4e36cef2",
|
"sha256": "d53dba8256fd6f7b62d1ce9bbad741af087ae44fec942bf2782b9c8f4e36cef2",
|
||||||
"os": "darwin",
|
"os": "darwin",
|
||||||
"arch": "universal",
|
"arch": "universal",
|
||||||
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.dmg",
|
"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"
|
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.app.zip"
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/android/Navis.apk",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/android/Navis.apk",
|
||||||
"sha256": "4416770bebe576eba42811990c4e4424f6722dea3a8513e6164dd34fc975b3cf",
|
"sha256": "4416770bebe576eba42811990c4e4424f6722dea3a8513e6164dd34fc975b3cf",
|
||||||
"os": "android",
|
"os": "android",
|
||||||
"arch": "universal"
|
"arch": "universal"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -470,13 +470,11 @@ func (a *App) AutoCheckUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// allowedLinkHosts are the only hosts the UI may open in the default browser
|
// 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).
|
// Competitor links are plain URLs without referral codes (Store policy).
|
||||||
var allowedLinkHosts = map[string]bool{
|
var allowedLinkHosts = map[string]bool{
|
||||||
"evilfox.win": true,
|
"evilfox.win": true,
|
||||||
"t.me": true,
|
"t.me": true,
|
||||||
"de4ima.uk": true,
|
|
||||||
"git.de4ima.uk": true,
|
|
||||||
"nordvpn.com": true,
|
"nordvpn.com": true,
|
||||||
"expressvpn.com": true,
|
"expressvpn.com": true,
|
||||||
"surfshark.com": true,
|
"surfshark.com": true,
|
||||||
|
|||||||
+153
-165
@@ -273,8 +273,9 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 18px 20px 8px;
|
padding: 18px 20px 8px;
|
||||||
}
|
}
|
||||||
.page { display: none; max-width: 640px; margin: 0 auto; animation: rise .3s var(--ease) both; }
|
/* Inactive pages must not paint (WebView2 overflow ghosts from hidden siblings). */
|
||||||
.page.active { display: block; }
|
.page { display: none !important; max-width: 640px; margin: 0 auto; }
|
||||||
|
.page.active { display: block !important; animation: rise .3s var(--ease) both; }
|
||||||
@keyframes rise {
|
@keyframes rise {
|
||||||
from { opacity: 0; transform: translateY(10px); }
|
from { opacity: 0; transform: translateY(10px); }
|
||||||
to { opacity: 1; transform: none; }
|
to { opacity: 1; transform: none; }
|
||||||
@@ -308,9 +309,12 @@
|
|||||||
.update-actions .action { flex: 1 1 auto; min-width: 140px; }
|
.update-actions .action { flex: 1 1 auto; min-width: 140px; }
|
||||||
|
|
||||||
/* ==== Home / power ==== */
|
/* ==== Home / power ==== */
|
||||||
|
/* Sticky so the big connect control never scrolls under the provider list. */
|
||||||
.hero {
|
.hero {
|
||||||
position: relative;
|
position: sticky;
|
||||||
overflow: hidden;
|
top: 0;
|
||||||
|
z-index: 6;
|
||||||
|
overflow: visible;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
background: var(--hero-bg);
|
background: var(--hero-bg);
|
||||||
@@ -318,6 +322,8 @@
|
|||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: border-color .25s, box-shadow .25s, background .25s;
|
transition: border-color .25s, box-shadow .25s, background .25s;
|
||||||
|
min-height: 220px;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.hero.on {
|
.hero.on {
|
||||||
border-color: rgba(13,138,102,.35);
|
border-color: rgba(13,138,102,.35);
|
||||||
@@ -327,6 +333,8 @@
|
|||||||
.power-btn {
|
.power-btn {
|
||||||
width: 148px;
|
width: 148px;
|
||||||
height: 148px;
|
height: 148px;
|
||||||
|
min-width: 148px;
|
||||||
|
min-height: 148px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 0;
|
border: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -341,6 +349,7 @@
|
|||||||
transition: background .25s, color .25s, transform .12s, box-shadow .25s;
|
transition: background .25s, color .25s, transform .12s, box-shadow .25s;
|
||||||
box-shadow: 0 14px 34px rgba(8, 145, 178, .3);
|
box-shadow: 0 14px 34px rgba(8, 145, 178, .3);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.power-btn:hover { transform: translateY(-2px); }
|
.power-btn:hover { transform: translateY(-2px); }
|
||||||
.power-btn:active { transform: none; }
|
.power-btn:active { transform: none; }
|
||||||
@@ -1336,7 +1345,7 @@
|
|||||||
<div class="brand-row">
|
<div class="brand-row">
|
||||||
<div class="logo" aria-hidden="true"><span class="fox">🦊</span></div>
|
<div class="logo" aria-hidden="true"><span class="fox">🦊</span></div>
|
||||||
<h1 class="brand">EvilFox</h1>
|
<h1 class="brand">EvilFox</h1>
|
||||||
<span class="badge-ver" id="badgeVer">4.2.0</span>
|
<span class="badge-ver" id="badgeVer" hidden></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="nav-btn active" type="button" data-page="guard">
|
<button class="nav-btn active" type="button" data-page="guard">
|
||||||
@@ -1362,7 +1371,7 @@
|
|||||||
|
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<div class="side-foot">
|
<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="Переключить тему">
|
<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">
|
<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"/>
|
<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="page active" data-page="guard">
|
||||||
<section class="hero" id="hero">
|
<section class="hero" id="hero">
|
||||||
<button class="power-btn" id="toggleBtn" type="button" aria-label="Подключить / отключить">
|
<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">
|
<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="M12 3v8"/>
|
||||||
<path d="M6.3 6.6a8 8 0 1 0 11.4 0"/>
|
<path d="M6.3 6.6a8 8 0 1 0 11.4 0"/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -1404,7 +1413,7 @@
|
|||||||
<div class="chips-line">
|
<div class="chips-line">
|
||||||
<span class="mode-chip" id="vpnChip" hidden>VPN · весь трафик</span>
|
<span class="mode-chip" id="vpnChip" hidden>VPN · весь трафик</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="hero-hint" id="heroHint">Нажмите, чтобы включить защиту</p>
|
<p class="hero-hint" id="heroHint">Нажмите большую кнопку, чтобы включить защиту</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<button class="cur-server" id="curServer" type="button" title="Открыть список серверов">
|
<button class="cur-server" id="curServer" type="button" title="Открыть список серверов">
|
||||||
@@ -1428,37 +1437,33 @@
|
|||||||
<div class="traffic-sub" id="trafficExp" hidden></div>
|
<div class="traffic-sub" id="trafficExp" hidden></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section" id="provSection">
|
||||||
<div class="section-title"><span>Выберите провайдера</span></div>
|
<div class="section-title"><span>Провайдеры</span></div>
|
||||||
<div class="prov-grid" id="provGrid">
|
<div class="prov-grid" id="provGrid">
|
||||||
<div class="prov-card ours">
|
<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-name">🦊 EvilFox VPN</div>
|
||||||
<div class="prov-sub">Оптимальные серверы рядом с вами · защита без настройки</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="action primary" id="provConnectBtn" type="button">🔥 Подключиться</button>
|
||||||
<button class="prov-ping" id="provPingOurs" type="button">📊 <span class="val">Проверить скорость</span></button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="prov-card">
|
<div class="prov-card">
|
||||||
<span class="prov-tag">Сторонний сервис</span>
|
<span class="prov-tag">Сторонний</span>
|
||||||
<div class="prov-name">NordVPN</div>
|
<div class="prov-name">NordVPN</div>
|
||||||
<div class="prov-sub">Оплата и настройка на сайте провайдера</div>
|
<div class="prov-sub">Оплата и настройка на сайте провайдера</div>
|
||||||
<button class="prov-go" type="button" data-url="https://nordvpn.com/" data-name="NordVPN">Перейти на сайт</button>
|
<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>
|
||||||
<div class="prov-card">
|
<div class="prov-card">
|
||||||
<span class="prov-tag">Сторонний сервис</span>
|
<span class="prov-tag">Сторонний</span>
|
||||||
<div class="prov-name">ExpressVPN</div>
|
<div class="prov-name">ExpressVPN</div>
|
||||||
<div class="prov-sub">Оплата и настройка на сайте провайдера</div>
|
<div class="prov-sub">Оплата и настройка на сайте провайдера</div>
|
||||||
<button class="prov-go" type="button" data-url="https://expressvpn.com/" data-name="ExpressVPN">Перейти на сайт</button>
|
<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>
|
||||||
<div class="prov-card">
|
<div class="prov-card">
|
||||||
<span class="prov-tag">Сторонний сервис</span>
|
<span class="prov-tag">Сторонний</span>
|
||||||
<div class="prov-name">Surfshark</div>
|
<div class="prov-name">Surfshark</div>
|
||||||
<div class="prov-sub">Оплата и настройка на сайте провайдера</div>
|
<div class="prov-sub">Оплата и настройка на сайте провайдера</div>
|
||||||
<button class="prov-go" type="button" data-url="https://surfshark.com/" data-name="Surfshark">Перейти на сайт</button>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1478,12 +1483,10 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="server-toolbar">
|
<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">
|
<div class="server-actions">
|
||||||
<button class="mini" id="pingBtn" type="button">Пинг</button>
|
<button class="mini" id="pingBtn" type="button">Пинг</button>
|
||||||
<button class="mini accent" id="bestBtn" type="button">Лучший</button>
|
<button class="mini accent" id="bestBtn" type="button" title="Выбрать сервер с наименьшим пингом">Выбрать лучший</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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="server-list" id="serverList"></div>
|
<div class="server-list" id="serverList"></div>
|
||||||
@@ -1507,18 +1510,16 @@
|
|||||||
|
|
||||||
<details class="profile-edit" id="profileEdit">
|
<details class="profile-edit" id="profileEdit">
|
||||||
<summary>Свойства сервера</summary>
|
<summary>Свойства сервера</summary>
|
||||||
<div class="stack" style="margin-top:8px">
|
<div class="stack" style="margin-top:8px" id="profProps">
|
||||||
<p class="host-line">Адрес сервера: <span id="profHost">—</span></p>
|
<div class="subinfo-row"><span class="k">Сервер</span><span class="v" id="propName">—</span></div>
|
||||||
<div>
|
<div class="subinfo-row"><span class="k">Пинг</span><span class="v" id="propPing">—</span></div>
|
||||||
<label class="field" for="name">Название</label>
|
<div class="subinfo-row"><span class="k">Протокол</span><span class="v" id="propProto">—</span></div>
|
||||||
<input id="name" type="text" placeholder="Например: DE-1 / Home" spellcheck="false" />
|
<div class="subinfo-row"><span class="k">Статус</span><span class="v" id="propStatus">—</span></div>
|
||||||
</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>
|
</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>
|
</details>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
@@ -1641,11 +1642,11 @@
|
|||||||
|
|
||||||
<details class="panel" id="advBox">
|
<details class="panel" id="advBox">
|
||||||
<summary>Расширенные настройки</summary>
|
<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>
|
<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">HTTP: <span id="advHttp">—</span></p>
|
||||||
<p class="adv-line">SOCKS: <span id="advSocks">—</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>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</section>
|
</section>
|
||||||
@@ -1707,7 +1708,7 @@
|
|||||||
</details>
|
</details>
|
||||||
<details class="panel">
|
<details class="panel">
|
||||||
<summary>Не подключается — что делать?</summary>
|
<summary>Не подключается — что делать?</summary>
|
||||||
<p class="remna-hint" style="margin-top:6px">Проверьте, что конфигурация добавлена и срок её действия не истёк («Мои серверы» → «Информация об аккаунте»). Попробуйте другой сервер (кнопка «Лучший»). Подробности — в журнале на вкладке «Статистика».</p>
|
<p class="remna-hint" style="margin-top:6px">Проверьте, что конфигурация добавлена и срок её действия не истёк («Мои серверы» → «Информация об аккаунте»). Попробуйте другой сервер (кнопка «Выбрать лучший»). Подробности — в журнале на вкладке «Статистика».</p>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<div class="about-card">
|
<div class="about-card">
|
||||||
@@ -1725,10 +1726,9 @@
|
|||||||
<button class="action primary" id="shopBtn" type="button">Перейти на сайт</button>
|
<button class="action primary" id="shopBtn" type="button">Перейти на сайт</button>
|
||||||
<button class="shop-link" id="shopLink" type="button">https://evilfox.win/</button>
|
<button class="shop-link" id="shopLink" type="button">https://evilfox.win/</button>
|
||||||
</section>
|
</section>
|
||||||
<!-- Рекомендуемые сервисы: ненавязчивый блок в нижнем углу «О программе».
|
<!-- Полезные ссылки (сайт + поддержка). Хосты — в allowlist openURL. -->
|
||||||
Ссылки задаются в скрипте — константа RECOMMENDED_SERVICES. -->
|
<section class="svc" aria-label="Полезные ссылки">
|
||||||
<section class="svc" aria-label="Рекомендуемые сервисы">
|
<h4>🌐 Полезные ссылки</h4>
|
||||||
<h4>Рекомендуемые сервисы</h4>
|
|
||||||
<div class="svc-grid" id="svcGrid"></div>
|
<div class="svc-grid" id="svcGrid"></div>
|
||||||
</section>
|
</section>
|
||||||
<button class="shop-link" id="quitBtn" type="button" hidden style="display:none;margin:12px auto 0;width:auto">Выйти</button>
|
<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 btn = $("toggleBtn");
|
||||||
const coreBtn = $("coreBtn");
|
const coreBtn = $("coreBtn");
|
||||||
const saveBtn = $("saveBtn");
|
const saveBtn = $("saveBtn");
|
||||||
const addBtn = $("addBtn");
|
const addBtn = $("addBtn"); // optional — header + removed
|
||||||
const delBtn = $("delBtn");
|
const delBtn = $("delBtn"); // optional — header × removed
|
||||||
const proxy = $("proxy");
|
const proxy = $("proxy");
|
||||||
const nameInput = $("name");
|
const nameInput = $("name");
|
||||||
const sysproxy = $("sysproxy");
|
const sysproxy = $("sysproxy");
|
||||||
@@ -1843,10 +1843,30 @@
|
|||||||
PT: "Португалия", RO: "Румыния", RS: "Сербия", RU: "Россия", SE: "Швеция",
|
PT: "Португалия", RO: "Румыния", RS: "Сербия", RU: "Россия", SE: "Швеция",
|
||||||
SG: "Сингапур", SK: "Словакия", TR: "Турция", UA: "Украина", US: "США", ZA: "ЮАР"
|
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) {
|
function countryRu(cc) {
|
||||||
const c = String(cc || "").toUpperCase();
|
const c = String(cc || "").toUpperCase();
|
||||||
return COUNTRY_RU[c] || "";
|
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-флаги ====
|
// ==== SVG-флаги ====
|
||||||
// Windows не умеет эмодзи-флаги: Segoe UI Emoji рисует пары региональных
|
// Windows не умеет эмодзи-флаги: Segoe UI Emoji рисует пары региональных
|
||||||
// индикаторов буквами («BE», «LV»), поэтому рисуем настоящие SVG-флаги.
|
// индикаторов буквами («BE», «LV»), поэтому рисуем настоящие SVG-флаги.
|
||||||
@@ -1976,7 +1996,7 @@
|
|||||||
if (d.e) b += d.e;
|
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>';
|
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) {
|
function profileISO(p) {
|
||||||
if (!p) return "";
|
if (!p) return "";
|
||||||
const c = String(p.country || "").toUpperCase();
|
const c = String(p.country || "").toUpperCase();
|
||||||
@@ -1986,7 +2006,7 @@
|
|||||||
const a = rs[0].codePointAt(0) - 0x1f1e6, b = rs[1].codePointAt(0) - 0x1f1e6;
|
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);
|
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 для известной страны, буквенный код — фолбэк.
|
// Заполнить ячейку флага: SVG для известной страны, буквенный код — фолбэк.
|
||||||
function setFlagEl(el, p) {
|
function setFlagEl(el, p) {
|
||||||
@@ -2084,15 +2104,12 @@
|
|||||||
const SHOP_URL = "https://evilfox.win/";
|
const SHOP_URL = "https://evilfox.win/";
|
||||||
// Текст оффера — правьте здесь (показывается на карточке EvilFox на вкладке «Защита»).
|
// Текст оффера — правьте здесь (показывается на карточке EvilFox на вкладке «Защита»).
|
||||||
// Правила Store: никаких цен и упоминаний оплаты внутри приложения.
|
// Правила Store: никаких цен и упоминаний оплаты внутри приложения.
|
||||||
const OFFER_TEXT = "🚀 3 дня бесплатно";
|
const OFFER_TEXT = "🎁 3 дня бесплатного доступа";
|
||||||
// Список рекомендуемых сервисов — замените ссылки/названия здесь.
|
// Полезные ссылки (Помощь). Хост каждой ссылки — в allowlist openURL
|
||||||
// Хост каждой ссылки должен быть разрешён в allowlist openURL
|
// (internal/apphost/app.go → allowedLinkHosts).
|
||||||
// (internal/apphost/app.go → allowedLinkHosts), иначе ссылка не откроется.
|
|
||||||
const RECOMMENDED_SERVICES = [
|
const RECOMMENDED_SERVICES = [
|
||||||
{ icon: "🛒", name: "EvilFox Shop", url: "https://evilfox.win/" },
|
{ icon: "🌐", name: "Сайт", url: "https://evilfox.win/" },
|
||||||
{ icon: "✈️", name: "Telegram", url: "https://t.me/evilfox" },
|
{ 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" }
|
|
||||||
];
|
];
|
||||||
const AUTO_BEST_KEY = "navis.autoBest";
|
const AUTO_BEST_KEY = "navis.autoBest";
|
||||||
// Оффер на карточке EvilFox рендерим из константы, чтобы правка была в одном месте.
|
// Оффер на карточке EvilFox рендерим из константы, чтобы правка была в одном месте.
|
||||||
@@ -2268,7 +2285,7 @@
|
|||||||
|
|
||||||
async function persistHy2IfNeeded(protocol) {
|
async function persistHy2IfNeeded(protocol) {
|
||||||
const p = (protocol || "").toLowerCase();
|
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://")) {
|
if (p === "hysteria2" || link.startsWith("hysteria2://") || link.startsWith("hy2://")) {
|
||||||
await saveHy2(readHy2());
|
await saveHy2(readHy2());
|
||||||
}
|
}
|
||||||
@@ -2334,12 +2351,13 @@
|
|||||||
|
|
||||||
// Человекочитаемые имена серверов («Чехия — оптимальный сервер», «Германия — сервер 2»).
|
// Человекочитаемые имена серверов («Чехия — оптимальный сервер», «Германия — сервер 2»).
|
||||||
// Реальный host:port показываем только в свойствах сервера и в журнале.
|
// Реальный host:port показываем только в свойствах сервера и в журнале.
|
||||||
|
// ISO всегда через profileISO (бэкенд + JS-алиасы опечаток) → countryRu, никогда сырое имя панели.
|
||||||
let friendlyMap = {};
|
let friendlyMap = {};
|
||||||
function rebuildFriendlyNames() {
|
function rebuildFriendlyNames() {
|
||||||
friendlyMap = {};
|
friendlyMap = {};
|
||||||
const groups = {};
|
const groups = {};
|
||||||
profiles.forEach((p) => {
|
profiles.forEach((p) => {
|
||||||
const key = String(p.country || "").toUpperCase() || "__none__";
|
const key = profileISO(p) || "__none__";
|
||||||
(groups[key] = groups[key] || []).push(p);
|
(groups[key] = groups[key] || []).push(p);
|
||||||
});
|
});
|
||||||
Object.keys(groups).forEach((key) => {
|
Object.keys(groups).forEach((key) => {
|
||||||
@@ -2360,13 +2378,19 @@
|
|||||||
} else {
|
} else {
|
||||||
title = region ? (region + " — сервер " + n) : ("Сервер " + n);
|
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) {
|
function friendlyTitle(name) {
|
||||||
const f = friendlyMap[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) {
|
function fillProfiles(list, active) {
|
||||||
@@ -2481,10 +2505,7 @@
|
|||||||
|
|
||||||
const right = document.createElement("div");
|
const right = document.createElement("div");
|
||||||
right.className = "right";
|
right.className = "right";
|
||||||
// Протокол — крошечная буква-иконка с подсказкой; строчный «vless»
|
// Протокол — только на карточке активного сервера / в «Свойствах», не в строках списка.
|
||||||
// под каждой строкой не повторяем.
|
|
||||||
const protoBadge = protoLetterBadge(p.protocol);
|
|
||||||
if (protoBadge) right.appendChild(protoBadge);
|
|
||||||
const ms = document.createElement("span");
|
const ms = document.createElement("span");
|
||||||
const pr = pingMap[p.name];
|
const pr = pingMap[p.name];
|
||||||
if (pr && pr.ok) {
|
if (pr && pr.ok) {
|
||||||
@@ -2500,12 +2521,12 @@
|
|||||||
ms.title = "пинг не измерен";
|
ms.title = "пинг не измерен";
|
||||||
}
|
}
|
||||||
right.appendChild(ms);
|
right.appendChild(ms);
|
||||||
// [📊] — проверить скорость только этого сервера.
|
// [📊] — проверить пинг только этого сервера.
|
||||||
const pingOne = document.createElement("button");
|
const pingOne = document.createElement("button");
|
||||||
pingOne.type = "button";
|
pingOne.type = "button";
|
||||||
pingOne.className = "row-ping";
|
pingOne.className = "row-ping";
|
||||||
pingOne.textContent = "📊";
|
pingOne.textContent = "📊";
|
||||||
pingOne.title = "Проверить скорость этого сервера";
|
pingOne.title = "Проверить пинг этого сервера";
|
||||||
pingOne.addEventListener("click", (e) => {
|
pingOne.addEventListener("click", (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
runPingOne(p.name, pingOne, ms);
|
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) {
|
async function runPingOne(name, btnEl, msEl) {
|
||||||
if (btnEl.disabled) return;
|
if (btnEl.disabled) return;
|
||||||
@@ -2689,11 +2696,17 @@
|
|||||||
const daysLeft = Math.floor((d.getTime() - Date.now()) / 86400000);
|
const daysLeft = Math.floor((d.getTime() - Date.now()) / 86400000);
|
||||||
const v = $("subExpireVal");
|
const v = $("subExpireVal");
|
||||||
if (daysLeft < 0) {
|
if (daysLeft < 0) {
|
||||||
v.textContent = text + " · срок истёк";
|
v.textContent = text + " · Истёк";
|
||||||
v.classList.add("warn");
|
v.classList.add("warn");
|
||||||
} else {
|
} else if (daysLeft === 0) {
|
||||||
v.textContent = daysLeft <= 14 ? (text + " · осталось " + daysLeft + " дн.") : text;
|
v.textContent = text + " · Сегодня";
|
||||||
|
v.classList.add("warn");
|
||||||
|
} else if (daysLeft <= 14) {
|
||||||
|
v.textContent = text + " · осталось " + daysLeft + " дн.";
|
||||||
v.classList.toggle("warn", daysLeft <= 3);
|
v.classList.toggle("warn", daysLeft <= 3);
|
||||||
|
} else {
|
||||||
|
v.textContent = text;
|
||||||
|
v.classList.remove("warn");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2713,12 +2726,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderUpdate(u, version) {
|
function renderUpdate(u, version) {
|
||||||
const label = "EvilFox v" + (version || "?");
|
// Версия — только в «Помощь»; в сайдбаре бренд без номера.
|
||||||
verLabel.textContent = label;
|
if (verLabel) verLabel.textContent = "EvilFox";
|
||||||
if (aboutVer) aboutVer.textContent = "Версия " + (version || "?");
|
if (aboutVer) aboutVer.textContent = "Версия " + (version || "?");
|
||||||
if (badgeVer && version) {
|
if (badgeVer) {
|
||||||
const short = String(version).split("+")[0];
|
badgeVer.hidden = true;
|
||||||
badgeVer.textContent = short || version;
|
badgeVer.textContent = "";
|
||||||
}
|
}
|
||||||
if (storePackaged || (u && u.store_managed)) {
|
if (storePackaged || (u && u.store_managed)) {
|
||||||
updateBanner.classList.remove("show");
|
updateBanner.classList.remove("show");
|
||||||
@@ -2780,14 +2793,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const lock = connected || busy;
|
const lock = connected || busy;
|
||||||
proxy.disabled = lock;
|
if (proxy) proxy.disabled = lock;
|
||||||
nameInput.disabled = lock;
|
if (nameInput) nameInput.disabled = lock;
|
||||||
sysproxy.disabled = lock;
|
sysproxy.disabled = lock;
|
||||||
profile.disabled = lock;
|
profile.disabled = lock;
|
||||||
addBtn.disabled = lock;
|
if (addBtn) addBtn.disabled = lock;
|
||||||
delBtn.disabled = lock || ((state.profiles || profiles).length <= 1);
|
if (delBtn) delBtn.disabled = lock || ((state.profiles || profiles).length <= 1);
|
||||||
saveBtn.disabled = lock;
|
if (saveBtn) saveBtn.disabled = lock;
|
||||||
coreBtn.disabled = busy;
|
if (coreBtn) coreBtn.disabled = busy;
|
||||||
pingBtn.disabled = busy;
|
pingBtn.disabled = busy;
|
||||||
bestBtn.disabled = busy;
|
bestBtn.disabled = busy;
|
||||||
updCheckBtn.disabled = busy;
|
updCheckBtn.disabled = busy;
|
||||||
@@ -2808,8 +2821,8 @@
|
|||||||
|
|
||||||
if (syncForm || (!formHydrated && !dirty)) {
|
if (syncForm || (!formHydrated && !dirty)) {
|
||||||
const active = (state.profiles || []).find((p) => p.name === profile.value) || {};
|
const active = (state.profiles || []).find((p) => p.name === profile.value) || {};
|
||||||
nameInput.value = active.name || state.active_profile || "";
|
if (nameInput) nameInput.value = active.name || state.active_profile || "";
|
||||||
proxy.value = typeof state.proxy === "string" ? state.proxy : (active.proxy || "");
|
if (proxy) proxy.value = typeof state.proxy === "string" ? state.proxy : (active.proxy || "");
|
||||||
if (typeof state.system_proxy === "boolean") sysproxy.checked = state.system_proxy;
|
if (typeof state.system_proxy === "boolean") sysproxy.checked = state.system_proxy;
|
||||||
fillHy2(state.hy2);
|
fillHy2(state.hy2);
|
||||||
formHydrated = true;
|
formHydrated = true;
|
||||||
@@ -2826,10 +2839,13 @@
|
|||||||
const hy2 = (label === "hysteria2" || label === "hy2");
|
const hy2 = (label === "hysteria2" || label === "hy2");
|
||||||
if ($("hy2Box")) $("hy2Box").style.display = hy2 ? "" : "none";
|
if ($("hy2Box")) $("hy2Box").style.display = hy2 ? "" : "none";
|
||||||
|
|
||||||
// Технические адреса прокси — только в «Настройки → Расширенные» и на «Статистике».
|
// Параметры прокси — только когда подключены (без пустого «появится после…»).
|
||||||
const advHttp = $("advHttp"), advSocks = $("advSocks"), logsProxyLine = $("logsProxyLine");
|
const advHttp = $("advHttp"), advSocks = $("advSocks"), logsProxyLine = $("logsProxyLine");
|
||||||
if (advHttp) advHttp.textContent = state.http_proxy || "появится после подключения";
|
const proxyParamsBlock = $("proxyParamsBlock");
|
||||||
if (advSocks) advSocks.textContent = state.socks_proxy || "появится после подключения";
|
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) {
|
if (logsProxyLine) {
|
||||||
const parts = [];
|
const parts = [];
|
||||||
if (state.http_proxy) parts.push("HTTP " + state.http_proxy);
|
if (state.http_proxy) parts.push("HTTP " + state.http_proxy);
|
||||||
@@ -2838,7 +2854,7 @@
|
|||||||
logsProxyLine.textContent = parts.length ? ("Прокси: " + parts.join(" · ")) : "";
|
logsProxyLine.textContent = parts.length ? ("Прокси: " + parts.join(" · ")) : "";
|
||||||
}
|
}
|
||||||
renderProvConnect();
|
renderProvConnect();
|
||||||
renderProfHost();
|
renderProfProps();
|
||||||
|
|
||||||
// Нижняя полоса статуса — только для важных сообщений; постоянного
|
// Нижняя полоса статуса — только для важных сообщений; постоянного
|
||||||
// «Готово к подключению» больше нет (статус виден на большой кнопке).
|
// «Готово к подключению» больше нет (статус виден на большой кнопке).
|
||||||
@@ -2878,12 +2894,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Реальный host:port выбранного сервера — в «Свойствах сервера» (муторные детали).
|
// Свойства выбранного сервера: имя / пинг / протокол / статус.
|
||||||
function renderProfHost() {
|
function renderProfProps() {
|
||||||
const el = $("profHost");
|
|
||||||
if (!el) return;
|
|
||||||
const p = profiles.find((x) => x.name === profile.value);
|
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) {
|
async function refresh(opts) {
|
||||||
@@ -2949,23 +2983,31 @@
|
|||||||
await runBest(true);
|
await runBest(true);
|
||||||
} else {
|
} else {
|
||||||
setMeta("Подключение…");
|
setMeta("Подключение…");
|
||||||
await saveProfile(nameInput.value.trim() || profile.value, proxy.value.trim(), !!sysproxy.checked);
|
// Профиль уже выбран; скрытые поля name/proxy гидратируются из state.
|
||||||
await persistHy2IfNeeded();
|
// Не перезаписываем 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();
|
await connect();
|
||||||
setMeta("Защищённое соединение установлено", "ok");
|
setMeta("Защищённое соединение установлено", "ok");
|
||||||
}
|
}
|
||||||
} catch (e) { setMeta(String(e), "err"); }
|
} catch (e) { setMeta(String(e), "err"); }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
saveBtn.addEventListener("click", () => withBusy(async () => {
|
if (saveBtn) saveBtn.addEventListener("click", () => withBusy(async () => {
|
||||||
try {
|
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();
|
await persistHy2IfNeeded();
|
||||||
setMeta("Профиль сохранён", "ok");
|
setMeta("Профиль сохранён", "ok");
|
||||||
} catch (e) { setMeta(String(e), "err"); }
|
} catch (e) { setMeta(String(e), "err"); }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
coreBtn.addEventListener("click", () => withBusy(async () => {
|
if (coreBtn) coreBtn.addEventListener("click", () => withBusy(async () => {
|
||||||
try {
|
try {
|
||||||
setMeta("Скачивание official cores…");
|
setMeta("Скачивание official cores…");
|
||||||
const path = await installCore();
|
const path = await installCore();
|
||||||
@@ -2973,7 +3015,7 @@
|
|||||||
} catch (e) { setMeta(String(e), "err"); }
|
} catch (e) { setMeta(String(e), "err"); }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
delBtn.addEventListener("click", () => withBusy(async () => {
|
if (delBtn) delBtn.addEventListener("click", () => withBusy(async () => {
|
||||||
try {
|
try {
|
||||||
if (!confirm("Удалить сервер «" + friendlyTitle(profile.value) + "»?")) return;
|
if (!confirm("Удалить сервер «" + friendlyTitle(profile.value) + "»?")) return;
|
||||||
await deleteProfile(profile.value);
|
await deleteProfile(profile.value);
|
||||||
@@ -2983,7 +3025,7 @@
|
|||||||
} catch (e) { setMeta(String(e), "err"); }
|
} catch (e) { setMeta(String(e), "err"); }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
addBtn.addEventListener("click", () => {
|
if (addBtn) addBtn.addEventListener("click", () => {
|
||||||
$("newName").value = "";
|
$("newName").value = "";
|
||||||
$("newProxy").value = "";
|
$("newProxy").value = "";
|
||||||
modal.classList.add("open");
|
modal.classList.add("open");
|
||||||
@@ -3022,7 +3064,7 @@
|
|||||||
shopBtn.addEventListener("click", openShop);
|
shopBtn.addEventListener("click", openShop);
|
||||||
shopLink.addEventListener("click", openShop);
|
shopLink.addEventListener("click", openShop);
|
||||||
|
|
||||||
// «Рекомендуемые сервисы» — рендер из RECOMMENDED_SERVICES (см. константу выше).
|
// «Рекомендуемые сервисы» → «Полезные ссылки» (см. RECOMMENDED_SERVICES).
|
||||||
(function renderRecommendedServices() {
|
(function renderRecommendedServices() {
|
||||||
const grid = $("svcGrid");
|
const grid = $("svcGrid");
|
||||||
if (!grid) return;
|
if (!grid) return;
|
||||||
@@ -3097,60 +3139,6 @@
|
|||||||
if (!connected && !busy) btn.click();
|
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 () => {
|
subBtn.addEventListener("click", () => withBusy(async () => {
|
||||||
try {
|
try {
|
||||||
await runImportSubscription();
|
await runImportSubscription();
|
||||||
|
|||||||
@@ -141,7 +141,9 @@ var countryNames = []nameCode{
|
|||||||
{"сша", "US"}, {"америка", "US"},
|
{"сша", "US"}, {"америка", "US"},
|
||||||
|
|
||||||
// Частые опечатки в именах серверов на панелях (наблюдались в проде).
|
// Частые опечатки в именах серверов на панелях (наблюдались в проде).
|
||||||
{"румьиня", "RO"}, {"румыня", "RO"}, {"руминия", "RO"},
|
// Longer stems first (Contains): «румьиния» before «румьиня».
|
||||||
|
{"румьиния", "RO"}, {"румяння", "RO"}, {"румьиня", "RO"},
|
||||||
|
{"румыня", "RO"}, {"руминия", "RO"}, {"румыниа", "RO"},
|
||||||
|
|
||||||
// City → country hints (EN + RU).
|
// City → country hints (EN + RU).
|
||||||
{"frankfurt", "DE"}, {"amsterdam", "NL"}, {"london", "GB"},
|
{"frankfurt", "DE"}, {"amsterdam", "NL"}, {"london", "GB"},
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ func TestFromText(t *testing.T) {
|
|||||||
// Panel-side typos seen in production.
|
// Panel-side typos seen in production.
|
||||||
{"Румьиня", "RO"},
|
{"Румьиня", "RO"},
|
||||||
{"Румыня-2", "RO"},
|
{"Румыня-2", "RO"},
|
||||||
|
{"Румяння", "RO"},
|
||||||
|
{"Румяння — оптимальный", "RO"},
|
||||||
|
{"Румьиния", "RO"},
|
||||||
|
{"Румьиния Fast", "RO"},
|
||||||
|
{"румыниа", "RO"},
|
||||||
|
{"Румыния", "RO"},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
cc, emoji := FromText(c.in)
|
cc, emoji := FromText(c.in)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
// CurrentVersion is the product/semver used for update eligibility (feed "version").
|
// CurrentVersion is the product/semver used for update eligibility (feed "version").
|
||||||
// Keep major.minor.patch only — no build suffix here.
|
// 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,
|
// BuildNumber is the monotonic build within CurrentVersion (Windows FileVersion 4th part,
|
||||||
// macOS CFBundleVersion suffix, Android versionCode low digits). Bump on every release build.
|
// macOS CFBundleVersion suffix, Android versionCode low digits). Bump on every release build.
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<Identity
|
<Identity
|
||||||
Name="EvilFox.EvilFox"
|
Name="EvilFox.EvilFox"
|
||||||
Publisher="CN=EvilFox"
|
Publisher="CN=EvilFox"
|
||||||
Version="4.2.0.1"
|
Version="4.2.1.1"
|
||||||
ProcessorArchitecture="x64" />
|
ProcessorArchitecture="x64" />
|
||||||
|
|
||||||
<Properties>
|
<Properties>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
param(
|
param(
|
||||||
[string]$ExePath = "",
|
[string]$ExePath = "",
|
||||||
[string]$OutDir = "",
|
[string]$OutDir = "",
|
||||||
[string]$Version = "4.2.0.1",
|
[string]$Version = "4.2.1.1",
|
||||||
[string]$Name = "EvilFox.EvilFox",
|
[string]$Name = "EvilFox.EvilFox",
|
||||||
[string]$Publisher = "CN=EvilFox",
|
[string]$Publisher = "CN=EvilFox",
|
||||||
[string]$DisplayName = "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=...?>.
|
# Prefer exact placeholder swaps so we never touch <?xml version=...?>.
|
||||||
$manifestText = $manifestText.Replace('Name="EvilFox.EvilFox"', "Name=`"$Name`"")
|
$manifestText = $manifestText.Replace('Name="EvilFox.EvilFox"', "Name=`"$Name`"")
|
||||||
$manifestText = $manifestText.Replace('Publisher="CN=EvilFox"', "Publisher=`"$Publisher`"")
|
$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('<DisplayName>EvilFox</DisplayName>', "<DisplayName>$DisplayName</DisplayName>")
|
||||||
$manifestText = $manifestText.Replace('<PublisherDisplayName>EvilFox</PublisherDisplayName>', "<PublisherDisplayName>$PublisherDisplayName</PublisherDisplayName>")
|
$manifestText = $manifestText.Replace('<PublisherDisplayName>EvilFox</PublisherDisplayName>', "<PublisherDisplayName>$PublisherDisplayName</PublisherDisplayName>")
|
||||||
$manifestText = $manifestText.Replace('DisplayName="EvilFox"', "DisplayName=`"$DisplayName`"")
|
$manifestText = $manifestText.Replace('DisplayName="EvilFox"', "DisplayName=`"$DisplayName`"")
|
||||||
|
|||||||
+46
-46
@@ -1,48 +1,48 @@
|
|||||||
{
|
{
|
||||||
"version": "4.2.0",
|
"version": "4.2.1",
|
||||||
"notes": "4.2.0: обновлённые формулировки, Kill Switch, статистика, кнопка подключения",
|
"notes": "4.2.1: исправлены опечатки Румынии (флаг RO), чистая шапка списка серверов, полезные ссылки вместо рекомендаций, убраны мусорные подписи на Защите, большая кнопка ПОДКЛЮЧИТЬСЯ всегда видна, свойства сервера заполнены, без T/V бейджей в списке",
|
||||||
"platform": "windows-amd64",
|
"platform": "windows-amd64",
|
||||||
"os": "windows",
|
"os": "windows",
|
||||||
"arch": "amd64",
|
"arch": "amd64",
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
|
||||||
"sha256": "64da79ddf7cd5694bf93c4c110bf0e1281a20651e1e868a960562d5c1a15bd0d",
|
"sha256": "c3885d6d0c5b6e542fe1a172650c2f9480afde2b9016ceb1a5699f851761b837",
|
||||||
"mandatory": false,
|
"mandatory": false,
|
||||||
"platforms": {
|
"platforms": {
|
||||||
"windows-amd64": {
|
"windows-amd64": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
|
||||||
"sha256": "64da79ddf7cd5694bf93c4c110bf0e1281a20651e1e868a960562d5c1a15bd0d",
|
"sha256": "c3885d6d0c5b6e542fe1a172650c2f9480afde2b9016ceb1a5699f851761b837",
|
||||||
"os": "windows",
|
"os": "windows",
|
||||||
"arch": "amd64"
|
"arch": "amd64"
|
||||||
},
|
},
|
||||||
"darwin-arm64": {
|
"darwin-arm64": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis",
|
||||||
"sha256": "01a40b1cce4e2fb3a38eb80f0815c295a00af11e4288f3b9e5c0b58ae336f28d",
|
"sha256": "01a40b1cce4e2fb3a38eb80f0815c295a00af11e4288f3b9e5c0b58ae336f28d",
|
||||||
"os": "darwin",
|
"os": "darwin",
|
||||||
"arch": "arm64",
|
"arch": "arm64",
|
||||||
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.dmg",
|
"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"
|
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/arm64/Navis.app.zip"
|
||||||
},
|
},
|
||||||
"darwin-amd64": {
|
"darwin-amd64": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis",
|
||||||
"sha256": "19a066cfc7f7c3a291a220482b28e66a459d091c19c5351818c401b982bc65b3",
|
"sha256": "19a066cfc7f7c3a291a220482b28e66a459d091c19c5351818c401b982bc65b3",
|
||||||
"os": "darwin",
|
"os": "darwin",
|
||||||
"arch": "amd64",
|
"arch": "amd64",
|
||||||
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.dmg",
|
"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"
|
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/amd64/Navis.app.zip"
|
||||||
},
|
},
|
||||||
"darwin-universal": {
|
"darwin-universal": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis",
|
||||||
"sha256": "d53dba8256fd6f7b62d1ce9bbad741af087ae44fec942bf2782b9c8f4e36cef2",
|
"sha256": "d53dba8256fd6f7b62d1ce9bbad741af087ae44fec942bf2782b9c8f4e36cef2",
|
||||||
"os": "darwin",
|
"os": "darwin",
|
||||||
"arch": "universal",
|
"arch": "universal",
|
||||||
"dmg_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.dmg",
|
"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"
|
"zip_url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/macos/universal/Navis.app.zip"
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/android/Navis.apk",
|
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/android/Navis.apk",
|
||||||
"sha256": "4416770bebe576eba42811990c4e4424f6722dea3a8513e6164dd34fc975b3cf",
|
"sha256": "4416770bebe576eba42811990c4e4424f6722dea3a8513e6164dd34fc975b3cf",
|
||||||
"os": "android",
|
"os": "android",
|
||||||
"arch": "universal"
|
"arch": "universal"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"FixedFileInfo": {
|
"FixedFileInfo": {
|
||||||
"FileVersion": { "Major": 4, "Minor": 2, "Patch": 0, "Build": 1 },
|
"FileVersion": { "Major": 4, "Minor": 2, "Patch": 1, "Build": 1 },
|
||||||
"ProductVersion": { "Major": 4, "Minor": 2, "Patch": 0, "Build": 1 },
|
"ProductVersion": { "Major": 4, "Minor": 2, "Patch": 1, "Build": 1 },
|
||||||
"FileFlagsMask": "3f",
|
"FileFlagsMask": "3f",
|
||||||
"FileFlags": "00",
|
"FileFlags": "00",
|
||||||
"FileOS": "40004",
|
"FileOS": "40004",
|
||||||
@@ -11,12 +11,12 @@
|
|||||||
"StringFileInfo": {
|
"StringFileInfo": {
|
||||||
"CompanyName": "EvilFox",
|
"CompanyName": "EvilFox",
|
||||||
"FileDescription": "EvilFox — VPN client (Naive / Hy2 / AWG / VLESS / VMess / Trojan)",
|
"FileDescription": "EvilFox — VPN client (Naive / Hy2 / AWG / VLESS / VMess / Trojan)",
|
||||||
"FileVersion": "4.2.0.1",
|
"FileVersion": "4.2.1.1",
|
||||||
"InternalName": "EvilFox",
|
"InternalName": "EvilFox",
|
||||||
"LegalCopyright": "Copyright (c) EvilFox",
|
"LegalCopyright": "Copyright (c) EvilFox",
|
||||||
"OriginalFilename": "EvilFox.exe",
|
"OriginalFilename": "EvilFox.exe",
|
||||||
"ProductName": "EvilFox",
|
"ProductName": "EvilFox",
|
||||||
"ProductVersion": "4.2.0.1",
|
"ProductVersion": "4.2.1.1",
|
||||||
"Comments": "Open-source VPN/proxy client. https://evilfox.win/"
|
"Comments": "Open-source VPN/proxy client. https://evilfox.win/"
|
||||||
},
|
},
|
||||||
"VarFileInfo": {
|
"VarFileInfo": {
|
||||||
|
|||||||
Reference in New Issue
Block a user