Release 4.1.1+1: UI polish по чек-листу ревью — трафик «Доступно X ГБ» + процент использования (баг «Осталось 100 из 100 · 0%»), оффер «3 дня бесплатно, затем от 99 ₽/мес», бейдж «Рекомендуем» на карточке EvilFox + подпись «Сторонний сервис» у конкурентов, пилюля «АКТИВЕН» и протокол мелким текстом в списке серверов, «—» вместо пустого пинга, счётчик серверов только на «Мои серверы», 🦊 вместо буквы N (старый логотип Navis), флаги во всех строках, geoflag-алиасы опечаток («Румьиня» → RO), фикс [hidden] vs display:grid. Windows 4.1.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_000_101
|
versionCode = 4_010_101
|
||||||
versionName = "4.0.1+1"
|
versionName = "4.1.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
+4
-4
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"notes": "4.1.0: новый интерфейс для Microsoft Store — вкладки Защита / Мои серверы / Статистика / Помощь, экран выбора провайдера, активация ключа, человекочитаемые имена серверов",
|
"notes": "4.1.1: исправлено отображение трафика, улучшены карточки провайдеров и список серверов",
|
||||||
"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": "6a6a0e80327e6aa87dd4c75b3fdb0795f0b7faf4d54481a73a99c31358cf492f",
|
"sha256": "362be966c08b95991deb3d7f47c839493fa1ef4cbc0f0325fb50f3d40b1f6225",
|
||||||
"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": "6a6a0e80327e6aa87dd4c75b3fdb0795f0b7faf4d54481a73a99c31358cf492f",
|
"sha256": "362be966c08b95991deb3d7f47c839493fa1ef4cbc0f0325fb50f3d40b1f6225",
|
||||||
"os": "windows",
|
"os": "windows",
|
||||||
"arch": "amd64"
|
"arch": "amd64"
|
||||||
},
|
},
|
||||||
|
|||||||
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
+4
-4
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"notes": "4.1.0: новый интерфейс для Microsoft Store — вкладки Защита / Мои серверы / Статистика / Помощь, экран выбора провайдера, активация ключа, человекочитаемые имена серверов",
|
"notes": "4.1.1: исправлено отображение трафика, улучшены карточки провайдеров и список серверов",
|
||||||
"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": "6a6a0e80327e6aa87dd4c75b3fdb0795f0b7faf4d54481a73a99c31358cf492f",
|
"sha256": "362be966c08b95991deb3d7f47c839493fa1ef4cbc0f0325fb50f3d40b1f6225",
|
||||||
"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": "6a6a0e80327e6aa87dd4c75b3fdb0795f0b7faf4d54481a73a99c31358cf492f",
|
"sha256": "362be966c08b95991deb3d7f47c839493fa1ef4cbc0f0325fb50f3d40b1f6225",
|
||||||
"os": "windows",
|
"os": "windows",
|
||||||
"arch": "amd64"
|
"arch": "amd64"
|
||||||
},
|
},
|
||||||
|
|||||||
+141
-48
@@ -136,6 +136,8 @@
|
|||||||
--log-bg: rgba(2, 6, 14, 0.6);
|
--log-bg: rgba(2, 6, 14, 0.6);
|
||||||
}
|
}
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
|
/* Атрибут hidden должен побеждать классовые display:grid/flex (иначе пустые карточки видны). */
|
||||||
|
[hidden] { display: none !important; }
|
||||||
html, body {
|
html, body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -180,6 +182,7 @@
|
|||||||
place-items: center;
|
place-items: center;
|
||||||
}
|
}
|
||||||
.logo svg { width: 26px; height: 26px; }
|
.logo svg { width: 26px; height: 26px; }
|
||||||
|
.logo .fox { font-size: 1.35rem; line-height: 1; }
|
||||||
.brand {
|
.brand {
|
||||||
font-family: Outfit, sans-serif;
|
font-family: Outfit, sans-serif;
|
||||||
font-size: 1.35rem;
|
font-size: 1.35rem;
|
||||||
@@ -529,6 +532,13 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
}
|
}
|
||||||
|
.section-title .count-note {
|
||||||
|
font-size: .68rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: none;
|
||||||
|
letter-spacing: 0;
|
||||||
|
opacity: .75;
|
||||||
|
}
|
||||||
|
|
||||||
label.field {
|
label.field {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -839,22 +849,39 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
.server-row .right { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; flex: 0 0 auto; }
|
.server-row .right { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; flex: 0 0 auto; }
|
||||||
.server-row .proto {
|
.server-row .name-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
|
||||||
font-size: .62rem;
|
/* Пилюля «АКТИВЕН» вместо строчного текста «активный сервер» */
|
||||||
|
.badge-active {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
font-size: .58rem;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
letter-spacing: .04em;
|
letter-spacing: .06em;
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--accent-deep);
|
color: var(--accent-deep);
|
||||||
background: var(--accent-soft);
|
background: var(--accent-soft);
|
||||||
|
border: 1px solid rgba(8,145,178,.35);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
padding: 2px 6px;
|
padding: 2px 7px;
|
||||||
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
html[data-theme="dark"] .badge-active { color: var(--accent); }
|
||||||
.server-row .ms {
|
.server-row .ms {
|
||||||
font-size: .78rem;
|
font-size: .78rem;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
}
|
}
|
||||||
|
.server-row .ms::before {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
width: 7px; height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 5px;
|
||||||
|
vertical-align: 1px;
|
||||||
|
background: var(--dot-off);
|
||||||
|
}
|
||||||
|
.server-row .ms.good::before, .server-row .ms.ok::before { background: var(--ms-good); }
|
||||||
|
.server-row .ms.mid::before { background: var(--ms-mid); }
|
||||||
|
.server-row .ms.bad::before { background: var(--danger); }
|
||||||
.server-row .ms.ok { color: var(--ok); }
|
.server-row .ms.ok { color: var(--ok); }
|
||||||
.server-row .ms.good { color: var(--ms-good); }
|
.server-row .ms.good { color: var(--ms-good); }
|
||||||
.server-row .ms.mid { color: var(--ms-mid); }
|
.server-row .ms.mid { color: var(--ms-mid); }
|
||||||
@@ -1017,6 +1044,7 @@
|
|||||||
transition: width .4s var(--ease);
|
transition: width .4s var(--ease);
|
||||||
}
|
}
|
||||||
.traffic-fill.warn { background: linear-gradient(90deg, #d97706, #f59e0b); }
|
.traffic-fill.warn { background: linear-gradient(90deg, #d97706, #f59e0b); }
|
||||||
|
.traffic-sub { font-size: .74rem; color: var(--muted); }
|
||||||
|
|
||||||
/* ==== Провайдеры (вкладка «Защита») ==== */
|
/* ==== Провайдеры (вкладка «Защита») ==== */
|
||||||
.prov-grid {
|
.prov-grid {
|
||||||
@@ -1050,9 +1078,10 @@
|
|||||||
html[data-theme="dark"] .prov-card.ours {
|
html[data-theme="dark"] .prov-card.ours {
|
||||||
background: linear-gradient(150deg, rgba(34,211,238,.16), var(--surface-2) 70%);
|
background: linear-gradient(150deg, rgba(34,211,238,.16), var(--surface-2) 70%);
|
||||||
}
|
}
|
||||||
|
/* Бейдж «Рекомендуем» лежит НА карточке EvilFox (верхний угол), а не между карточками. */
|
||||||
.prov-badge {
|
.prov-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -9px;
|
top: 10px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
font-size: .64rem;
|
font-size: .64rem;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
@@ -1064,6 +1093,16 @@
|
|||||||
padding: 3px 9px;
|
padding: 3px 9px;
|
||||||
box-shadow: 0 4px 10px rgba(251, 191, 36, .35);
|
box-shadow: 0 4px 10px rgba(251, 191, 36, .35);
|
||||||
}
|
}
|
||||||
|
.prov-card.ours .prov-name { padding-right: 96px; }
|
||||||
|
/* Приглушённая подпись «Сторонний сервис» на карточках конкурентов */
|
||||||
|
.prov-tag {
|
||||||
|
font-size: .62rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: .05em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--muted);
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
.prov-gift { font-size: .72rem; font-weight: 700; color: var(--accent-deep); }
|
.prov-gift { font-size: .72rem; font-weight: 700; color: var(--accent-deep); }
|
||||||
html[data-theme="dark"] .prov-gift { color: var(--accent); }
|
html[data-theme="dark"] .prov-gift { color: var(--accent); }
|
||||||
.prov-card .action { padding: 10px 12px; font-size: .84rem; }
|
.prov-card .action { padding: 10px 12px; font-size: .84rem; }
|
||||||
@@ -1150,6 +1189,7 @@
|
|||||||
}
|
}
|
||||||
.about-card .logo { margin: 0 auto 10px; width: 56px; height: 56px; border-radius: 16px; }
|
.about-card .logo { margin: 0 auto 10px; width: 56px; height: 56px; border-radius: 16px; }
|
||||||
.about-card .logo svg { width: 34px; height: 34px; }
|
.about-card .logo svg { width: 34px; height: 34px; }
|
||||||
|
.about-card .logo .fox { font-size: 1.9rem; }
|
||||||
.about-card h2 { font-family: Outfit, sans-serif; margin: 0 0 4px; letter-spacing: -.03em; }
|
.about-card h2 { font-family: Outfit, sans-serif; margin: 0 0 4px; letter-spacing: -.03em; }
|
||||||
.about-card p { margin: 0; color: var(--muted); font-size: .84rem; line-height: 1.45; }
|
.about-card p { margin: 0; color: var(--muted); font-size: .84rem; line-height: 1.45; }
|
||||||
|
|
||||||
@@ -1191,14 +1231,9 @@
|
|||||||
<!-- ===== Sidebar ===== -->
|
<!-- ===== Sidebar ===== -->
|
||||||
<nav class="side">
|
<nav class="side">
|
||||||
<div class="brand-row">
|
<div class="brand-row">
|
||||||
<div class="logo" aria-hidden="true">
|
<div class="logo" aria-hidden="true"><span class="fox">🦊</span></div>
|
||||||
<svg viewBox="0 0 32 32" fill="none">
|
|
||||||
<path d="M8 26V6h4.2L20 18.4V6H24v20h-4.2L12 13.6V26H8z" fill="#f4fff9"/>
|
|
||||||
<path d="M22.5 8.2l3.2-1.1-1.1 3.2-5.6 5.6 1.6 1.6 5.6-5.6 3.2-1.1-1.1 3.2" fill="#b8f0d8" opacity=".9"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<h1 class="brand">EvilFox</h1>
|
<h1 class="brand">EvilFox</h1>
|
||||||
<span class="badge-ver" id="badgeVer">4.1.0</span>
|
<span class="badge-ver" id="badgeVer">4.1.1</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="nav-btn active" type="button" data-page="guard">
|
<button class="nav-btn active" type="button" data-page="guard">
|
||||||
@@ -1262,8 +1297,8 @@
|
|||||||
<span class="dot" id="dot"></span>
|
<span class="dot" id="dot"></span>
|
||||||
<span id="statusText">Отключено</span>
|
<span id="statusText">Отключено</span>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Протокол показываем на карточке активного сервера ниже — в статусной строке не дублируем. -->
|
||||||
<div class="chips-line">
|
<div class="chips-line">
|
||||||
<span class="proto-chip" id="protoChip">протокол</span>
|
|
||||||
<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>
|
||||||
@@ -1287,6 +1322,7 @@
|
|||||||
<span class="pct" id="trafficPct"></span>
|
<span class="pct" id="trafficPct"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="traffic-bar"><div class="traffic-fill" id="trafficFill"></div></div>
|
<div class="traffic-bar"><div class="traffic-fill" id="trafficFill"></div></div>
|
||||||
|
<div class="traffic-sub" id="trafficExp" hidden></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
@@ -1296,25 +1332,28 @@
|
|||||||
<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">🎁 Демо-доступ от 30₽</span>
|
<span class="prov-gift" id="provGift">🚀 3 дня бесплатно. Затем — от 99 ₽/мес</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>
|
<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>
|
||||||
<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>
|
<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>
|
||||||
<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>
|
<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>
|
||||||
<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>
|
<button class="prov-ping" type="button" data-base="55">📊 <span class="val">Проверить скорость</span></button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1335,7 +1374,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="server-toolbar">
|
<div class="server-toolbar">
|
||||||
<div class="section-title"><span>Серверы</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">Лучший</button>
|
||||||
@@ -1507,6 +1546,7 @@
|
|||||||
<span class="pct" id="trafficPct2"></span>
|
<span class="pct" id="trafficPct2"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="traffic-bar"><div class="traffic-fill" id="trafficFill2"></div></div>
|
<div class="traffic-bar"><div class="traffic-fill" id="trafficFill2"></div></div>
|
||||||
|
<div class="traffic-sub" id="trafficExp2" hidden></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="section-title" style="margin-top:4px"><span>Журнал подключения</span></div>
|
<div class="section-title" style="margin-top:4px"><span>Журнал подключения</span></div>
|
||||||
@@ -1536,12 +1576,7 @@
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
<div class="about-card">
|
<div class="about-card">
|
||||||
<div class="logo" aria-hidden="true">
|
<div class="logo" aria-hidden="true"><span class="fox">🦊</span></div>
|
||||||
<svg viewBox="0 0 32 32" fill="none">
|
|
||||||
<path d="M8 26V6h4.2L20 18.4V6H24v20h-4.2L12 13.6V26H8z" fill="#f4fff9"/>
|
|
||||||
<path d="M22.5 8.2l3.2-1.1-1.1 3.2-5.6 5.6 1.6 1.6 5.6-5.6 3.2-1.1-1.1 3.2" fill="#b8f0d8" opacity=".9"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<h2>EvilFox</h2>
|
<h2>EvilFox</h2>
|
||||||
<p id="aboutVer">версия…</p>
|
<p id="aboutVer">версия…</p>
|
||||||
<p style="margin-top:6px">VPN/прокси-клиент: Naive · Hysteria 2 · AWG · Xray</p>
|
<p style="margin-top:6px">VPN/прокси-клиент: Naive · Hysteria 2 · AWG · Xray</p>
|
||||||
@@ -1551,7 +1586,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<section class="shop" aria-label="Купить ключ">
|
<section class="shop" aria-label="Купить ключ">
|
||||||
<h3>Нужен ключ?</h3>
|
<h3>Нужен ключ?</h3>
|
||||||
<p>Ключи и тарифы на evilfox.win — демо от 30₽. Техподдержка — в Telegram.</p>
|
<p>Ключи и тарифы на evilfox.win — 3 дня бесплатно, затем от 99 ₽/мес. Техподдержка — в Telegram.</p>
|
||||||
<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>
|
||||||
@@ -1676,6 +1711,18 @@
|
|||||||
const c = String(cc || "").toUpperCase();
|
const c = String(cc || "").toUpperCase();
|
||||||
return COUNTRY_RU[c] || "";
|
return COUNTRY_RU[c] || "";
|
||||||
}
|
}
|
||||||
|
// "RO" → "🇷🇴" — запасной вариант, если бэкенд не прислал флаг.
|
||||||
|
function isoFlag(cc) {
|
||||||
|
const c = String(cc || "").toUpperCase();
|
||||||
|
if (!/^[A-Z]{2}$/.test(c)) return "";
|
||||||
|
return String.fromCodePoint(0x1f1e6 + c.charCodeAt(0) - 65, 0x1f1e6 + c.charCodeAt(1) - 65);
|
||||||
|
}
|
||||||
|
function pluralRu(n, one, few, many) {
|
||||||
|
const m10 = n % 10, m100 = n % 100;
|
||||||
|
if (m10 === 1 && m100 !== 11) return one;
|
||||||
|
if (m10 >= 2 && m10 <= 4 && (m100 < 12 || m100 > 14)) return few;
|
||||||
|
return many;
|
||||||
|
}
|
||||||
|
|
||||||
// ==== Navigation ====
|
// ==== Navigation ====
|
||||||
document.querySelectorAll(".nav-btn").forEach((b) => {
|
document.querySelectorAll(".nav-btn").forEach((b) => {
|
||||||
@@ -1748,6 +1795,8 @@
|
|||||||
const protoChip = $("protoChip");
|
const protoChip = $("protoChip");
|
||||||
const heroHint = $("heroHint");
|
const heroHint = $("heroHint");
|
||||||
const SHOP_URL = "https://evilfox.win/";
|
const SHOP_URL = "https://evilfox.win/";
|
||||||
|
// Текст оффера — правьте здесь (показывается на карточке EvilFox на вкладке «Защита»).
|
||||||
|
const OFFER_TEXT = "🚀 3 дня бесплатно. Затем — от 99 ₽/мес";
|
||||||
// Список рекомендуемых сервисов — замените ссылки/названия здесь.
|
// Список рекомендуемых сервисов — замените ссылки/названия здесь.
|
||||||
// Хост каждой ссылки должен быть разрешён в allowlist openURL
|
// Хост каждой ссылки должен быть разрешён в allowlist openURL
|
||||||
// (internal/apphost/app.go → allowedLinkHosts), иначе ссылка не откроется.
|
// (internal/apphost/app.go → allowedLinkHosts), иначе ссылка не откроется.
|
||||||
@@ -1758,6 +1807,8 @@
|
|||||||
{ icon: "🦊", name: "Git", url: "https://git.de4ima.uk/Evilfox/navi" }
|
{ icon: "🦊", name: "Git", url: "https://git.de4ima.uk/Evilfox/navi" }
|
||||||
];
|
];
|
||||||
const AUTO_BEST_KEY = "navis.autoBest";
|
const AUTO_BEST_KEY = "navis.autoBest";
|
||||||
|
// Оффер на карточке EvilFox рендерим из константы, чтобы правка была в одном месте.
|
||||||
|
if ($("provGift")) $("provGift").textContent = OFFER_TEXT;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const saved = localStorage.getItem(AUTO_BEST_KEY);
|
const saved = localStorage.getItem(AUTO_BEST_KEY);
|
||||||
@@ -1940,7 +1991,7 @@
|
|||||||
$("curPing").className = "ms";
|
$("curPing").className = "ms";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$("curFlag").textContent = p.flag || (p.country || "·");
|
$("curFlag").textContent = p.flag || isoFlag(p.country) || "·";
|
||||||
$("curName").textContent = friendlyTitle(p.name);
|
$("curName").textContent = friendlyTitle(p.name);
|
||||||
$("curHost").textContent = "нажмите, чтобы выбрать другой сервер";
|
$("curHost").textContent = "нажмите, чтобы выбрать другой сервер";
|
||||||
$("curProto").textContent = (p.protocol || "?").toString();
|
$("curProto").textContent = (p.protocol || "?").toString();
|
||||||
@@ -1949,13 +2000,16 @@
|
|||||||
if (pr && pr.ok) {
|
if (pr && pr.ok) {
|
||||||
el.className = "ms " + msClass(pr.ms, true);
|
el.className = "ms " + msClass(pr.ms, true);
|
||||||
el.textContent = pr.ms + " ms";
|
el.textContent = pr.ms + " ms";
|
||||||
|
el.title = "";
|
||||||
} else if (pr && pr.error) {
|
} else if (pr && pr.error) {
|
||||||
el.className = "ms bad";
|
el.className = "ms bad";
|
||||||
el.textContent = "—";
|
el.textContent = "—";
|
||||||
el.title = pr.error;
|
el.title = pr.error;
|
||||||
} else {
|
} else {
|
||||||
|
// Пинг ещё не измерялся — показываем «—», а не пустоту.
|
||||||
el.className = "ms";
|
el.className = "ms";
|
||||||
el.textContent = "…";
|
el.textContent = "—";
|
||||||
|
el.title = "пинг не измерен";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1963,6 +2017,13 @@
|
|||||||
const active = activeName || profile.value;
|
const active = activeName || profile.value;
|
||||||
serverList.innerHTML = "";
|
serverList.innerHTML = "";
|
||||||
const list = orderedProfiles();
|
const list = orderedProfiles();
|
||||||
|
// Количество серверов — только здесь, мелкой приглушённой подписью.
|
||||||
|
const srvCount = $("srvCount");
|
||||||
|
if (srvCount) {
|
||||||
|
srvCount.textContent = list.length
|
||||||
|
? "· " + list.length + " " + pluralRu(list.length, "сервер", "сервера", "серверов")
|
||||||
|
: "";
|
||||||
|
}
|
||||||
if (!list.length) {
|
if (!list.length) {
|
||||||
const empty = document.createElement("div");
|
const empty = document.createElement("div");
|
||||||
empty.className = "server-empty";
|
empty.className = "server-empty";
|
||||||
@@ -1971,35 +2032,44 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
list.forEach((p) => {
|
list.forEach((p) => {
|
||||||
|
const isActive = p.name === active;
|
||||||
const row = document.createElement("button");
|
const row = document.createElement("button");
|
||||||
row.type = "button";
|
row.type = "button";
|
||||||
row.className = "server-row" + (p.name === active ? " active" : "");
|
row.className = "server-row" + (isActive ? " active" : "");
|
||||||
row.dataset.name = p.name;
|
row.dataset.name = p.name;
|
||||||
|
|
||||||
const left = document.createElement("div");
|
const left = document.createElement("div");
|
||||||
left.className = "left";
|
left.className = "left";
|
||||||
|
const flagEmoji = p.flag || isoFlag(p.country);
|
||||||
const flag = document.createElement("span");
|
const flag = document.createElement("span");
|
||||||
flag.className = "flag" + (p.flag ? "" : " unknown");
|
flag.className = "flag" + (flagEmoji ? "" : " unknown");
|
||||||
flag.textContent = p.flag || (p.country || "·");
|
flag.textContent = flagEmoji || "·";
|
||||||
flag.title = p.country || "регион неизвестен";
|
flag.title = p.country || "регион неизвестен";
|
||||||
const metaCol = document.createElement("div");
|
const metaCol = document.createElement("div");
|
||||||
metaCol.className = "meta-col";
|
metaCol.className = "meta-col";
|
||||||
|
const nameRow = document.createElement("div");
|
||||||
|
nameRow.className = "name-row";
|
||||||
const nameEl = document.createElement("div");
|
const nameEl = document.createElement("div");
|
||||||
nameEl.className = "name";
|
nameEl.className = "name";
|
||||||
nameEl.textContent = friendlyTitle(p.name);
|
nameEl.textContent = friendlyTitle(p.name);
|
||||||
|
nameRow.appendChild(nameEl);
|
||||||
|
if (isActive) {
|
||||||
|
const badge = document.createElement("span");
|
||||||
|
badge.className = "badge-active";
|
||||||
|
badge.textContent = "АКТИВЕН";
|
||||||
|
nameRow.appendChild(badge);
|
||||||
|
}
|
||||||
|
// Протокол в списке — мелкой приглушённой подписью (чип есть на карточке активного сервера).
|
||||||
const sub = document.createElement("div");
|
const sub = document.createElement("div");
|
||||||
sub.className = "sub";
|
sub.className = "sub";
|
||||||
sub.textContent = p.name === active ? "активный сервер" : "защищённое соединение";
|
sub.textContent = (p.protocol || "").toString() || "защищённое соединение";
|
||||||
metaCol.appendChild(nameEl);
|
metaCol.appendChild(nameRow);
|
||||||
metaCol.appendChild(sub);
|
metaCol.appendChild(sub);
|
||||||
left.appendChild(flag);
|
left.appendChild(flag);
|
||||||
left.appendChild(metaCol);
|
left.appendChild(metaCol);
|
||||||
|
|
||||||
const right = document.createElement("div");
|
const right = document.createElement("div");
|
||||||
right.className = "right";
|
right.className = "right";
|
||||||
const proto = document.createElement("span");
|
|
||||||
proto.className = "proto";
|
|
||||||
proto.textContent = (p.protocol || "?").toString();
|
|
||||||
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) {
|
||||||
@@ -2011,9 +2081,9 @@
|
|||||||
ms.title = pr.error;
|
ms.title = pr.error;
|
||||||
} else {
|
} else {
|
||||||
ms.className = "ms";
|
ms.className = "ms";
|
||||||
ms.textContent = "…";
|
ms.textContent = "—";
|
||||||
|
ms.title = "пинг не измерен";
|
||||||
}
|
}
|
||||||
right.appendChild(proto);
|
|
||||||
right.appendChild(ms);
|
right.appendChild(ms);
|
||||||
|
|
||||||
row.appendChild(left);
|
row.appendChild(left);
|
||||||
@@ -2079,22 +2149,27 @@
|
|||||||
if (vpnChip) vpnChip.hidden = !state.vpn_active;
|
if (vpnChip) vpnChip.hidden = !state.vpn_active;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Прогресс-бар трафика на «Защите» и «Статистике» (📊 Осталось X из Y ГБ).
|
// Прогресс-бар трафика на «Защите» и «Статистике».
|
||||||
|
// Заголовок — «📊 Доступно X ГБ», полоса и подпись — процент ИЗРАСХОДОВАННОГО
|
||||||
|
// («Z% использовано»), чтобы цифры и полоса были в одной системе координат.
|
||||||
function renderTraffic(info) {
|
function renderTraffic(info) {
|
||||||
const hasTraffic = !!(info && (info.total || info.download || info.upload));
|
const hasTraffic = !!(info && (info.total || info.download || info.upload));
|
||||||
[["trafficCard", "trafficLabel", "trafficPct", "trafficFill"],
|
[["trafficCard", "trafficLabel", "trafficPct", "trafficFill", "trafficExp"],
|
||||||
["trafficCard2", "trafficLabel2", "trafficPct2", "trafficFill2"]].forEach((ids) => {
|
["trafficCard2", "trafficLabel2", "trafficPct2", "trafficFill2", "trafficExp2"]].forEach((ids) => {
|
||||||
const card = $(ids[0]);
|
const card = $(ids[0]);
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
if (!hasTraffic) { card.hidden = true; return; }
|
if (!hasTraffic) { card.hidden = true; return; }
|
||||||
card.hidden = false;
|
card.hidden = false;
|
||||||
const used = (info.upload || 0) + (info.download || 0);
|
const used = (info.upload || 0) + (info.download || 0);
|
||||||
const label = $(ids[1]), pct = $(ids[2]), fill = $(ids[3]);
|
const label = $(ids[1]), pct = $(ids[2]), fill = $(ids[3]), exp = $(ids[4]);
|
||||||
if (info.total > 0) {
|
if (info.total > 0) {
|
||||||
const left = Math.max(0, info.total - used);
|
const left = Math.max(0, info.total - used);
|
||||||
const usedPct = Math.min(100, Math.round(used / info.total * 100));
|
const usedPct = Math.min(100, Math.round(used / info.total * 100));
|
||||||
label.textContent = "Осталось " + fmtTraffic(left) + " из " + fmtTraffic(info.total);
|
// «из Y ГБ» показываем только когда лимит уже начали расходовать.
|
||||||
pct.textContent = usedPct + "%";
|
label.textContent = used > 0
|
||||||
|
? "Доступно " + fmtTraffic(left) + " из " + fmtTraffic(info.total)
|
||||||
|
: "Доступно " + fmtTraffic(left);
|
||||||
|
pct.textContent = usedPct + "% использовано";
|
||||||
fill.style.width = usedPct + "%";
|
fill.style.width = usedPct + "%";
|
||||||
fill.classList.toggle("warn", usedPct >= 85);
|
fill.classList.toggle("warn", usedPct >= 85);
|
||||||
} else {
|
} else {
|
||||||
@@ -2103,6 +2178,21 @@
|
|||||||
fill.style.width = "100%";
|
fill.style.width = "100%";
|
||||||
fill.classList.remove("warn");
|
fill.classList.remove("warn");
|
||||||
}
|
}
|
||||||
|
if (exp) {
|
||||||
|
let expText = "";
|
||||||
|
if (info.expire) {
|
||||||
|
const daysLeft = Math.floor((info.expire * 1000 - Date.now()) / 86400000);
|
||||||
|
if (daysLeft === 0) {
|
||||||
|
expText = "🔥 Сгорает сегодня";
|
||||||
|
} else if (daysLeft > 0) {
|
||||||
|
expText = "🔥 Сгорает через " + daysLeft + " " + pluralRu(daysLeft, "день", "дня", "дней");
|
||||||
|
} else {
|
||||||
|
expText = "Срок доступа истёк";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exp.hidden = !expText;
|
||||||
|
exp.textContent = expText;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2240,8 +2330,11 @@
|
|||||||
|
|
||||||
const active = (state.profiles || []).find((p) => p.name === profile.value) || {};
|
const active = (state.profiles || []).find((p) => p.name === profile.value) || {};
|
||||||
const label = detectProtoLabel(state, active);
|
const label = detectProtoLabel(state, active);
|
||||||
protoChip.textContent = label;
|
// Чип протокола в hero убран (дублировал карточку активного сервера) — guard на случай возврата.
|
||||||
protoChip.classList.toggle("active", !!state.connected || label !== "протокол");
|
if (protoChip) {
|
||||||
|
protoChip.textContent = label;
|
||||||
|
protoChip.classList.toggle("active", !!state.connected || label !== "протокол");
|
||||||
|
}
|
||||||
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";
|
||||||
|
|
||||||
@@ -2269,8 +2362,8 @@
|
|||||||
detail = "Сначала установите cores в Настройках («Сервис»)";
|
detail = "Сначала установите cores в Настройках («Сервис»)";
|
||||||
heroHint.textContent = "Нужны cores для выбранного протокола";
|
heroHint.textContent = "Нужны cores для выбранного протокола";
|
||||||
} else {
|
} else {
|
||||||
const n = (state.profiles || []).length;
|
// Количество серверов на главном экране не показываем — оно есть на «Мои серверы».
|
||||||
detail = n > 1 ? ("Серверов: " + n + " · «Мои серверы» → Лучший") : "Готово к подключению";
|
detail = "Готово к подключению";
|
||||||
heroHint.textContent = currentMode === "vpn"
|
heroHint.textContent = currentMode === "vpn"
|
||||||
? "Режим VPN: защита всего трафика, включая игры и приложения"
|
? "Режим VPN: защита всего трафика, включая игры и приложения"
|
||||||
: "Нажмите на кнопку, чтобы включить защиту";
|
: "Нажмите на кнопку, чтобы включить защиту";
|
||||||
|
|||||||
@@ -140,6 +140,9 @@ var countryNames = []nameCode{
|
|||||||
{"исландия", "IS"}, {"люксембург", "LU"}, {"англия", "GB"},
|
{"исландия", "IS"}, {"люксембург", "LU"}, {"англия", "GB"},
|
||||||
{"сша", "US"}, {"америка", "US"},
|
{"сша", "US"}, {"америка", "US"},
|
||||||
|
|
||||||
|
// Частые опечатки в именах серверов на панелях (наблюдались в проде).
|
||||||
|
{"румьиня", "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"},
|
||||||
{"paris", "FR"}, {"helsinki", "FI"}, {"stockholm", "SE"},
|
{"paris", "FR"}, {"helsinki", "FI"}, {"stockholm", "SE"},
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ func TestFromText(t *testing.T) {
|
|||||||
{"🇨🇿 Czech Republic Vless", "CZ"},
|
{"🇨🇿 Czech Republic Vless", "CZ"},
|
||||||
{"🇳🇴 Norway TR+WS", "NO"},
|
{"🇳🇴 Norway TR+WS", "NO"},
|
||||||
{"🇩🇪 Germany 3 HY", "DE"},
|
{"🇩🇪 Germany 3 HY", "DE"},
|
||||||
|
// Panel-side typos seen in production.
|
||||||
|
{"Румьиня", "RO"},
|
||||||
|
{"Румыня-2", "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.1.0"
|
const CurrentVersion = "4.1.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.1.0.1"
|
Version="4.1.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.0.1.1",
|
[string]$Version = "4.1.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.0.1.1"', "Version=`"$Version`"")
|
$manifestText = $manifestText.Replace('Version="4.1.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`"")
|
||||||
|
|||||||
+4
-4
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"notes": "4.1.0: новый интерфейс для Microsoft Store — вкладки Защита / Мои серверы / Статистика / Помощь, экран выбора провайдера, активация ключа, человекочитаемые имена серверов",
|
"notes": "4.1.1: исправлено отображение трафика, улучшены карточки провайдеров и список серверов",
|
||||||
"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": "6a6a0e80327e6aa87dd4c75b3fdb0795f0b7faf4d54481a73a99c31358cf492f",
|
"sha256": "362be966c08b95991deb3d7f47c839493fa1ef4cbc0f0325fb50f3d40b1f6225",
|
||||||
"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": "6a6a0e80327e6aa87dd4c75b3fdb0795f0b7faf4d54481a73a99c31358cf492f",
|
"sha256": "362be966c08b95991deb3d7f47c839493fa1ef4cbc0f0325fb50f3d40b1f6225",
|
||||||
"os": "windows",
|
"os": "windows",
|
||||||
"arch": "amd64"
|
"arch": "amd64"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
"vpnclient/internal/apphost"
|
"vpnclient/internal/apphost"
|
||||||
"vpnclient/internal/config"
|
"vpnclient/internal/config"
|
||||||
@@ -28,6 +29,10 @@ func main() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
a := apphost.New(mgr, cfgPath, logBuf)
|
a := apphost.New(mgr, cfgPath, logBuf)
|
||||||
fmt.Println("preview on http://127.0.0.1:8765/")
|
addr := os.Getenv("NAVIS_PREVIEW_ADDR")
|
||||||
log.Fatal(http.ListenAndServe("127.0.0.1:8765", a.Handler()))
|
if addr == "" {
|
||||||
|
addr = "127.0.0.1:8765" // may clash with a running app — override via NAVIS_PREVIEW_ADDR
|
||||||
|
}
|
||||||
|
fmt.Println("preview on http://" + addr + "/")
|
||||||
|
log.Fatal(http.ListenAndServe(addr, a.Handler()))
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"FixedFileInfo": {
|
"FixedFileInfo": {
|
||||||
"FileVersion": { "Major": 4, "Minor": 1, "Patch": 0, "Build": 1 },
|
"FileVersion": { "Major": 4, "Minor": 1, "Patch": 1, "Build": 1 },
|
||||||
"ProductVersion": { "Major": 4, "Minor": 1, "Patch": 0, "Build": 1 },
|
"ProductVersion": { "Major": 4, "Minor": 1, "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.1.0.1",
|
"FileVersion": "4.1.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.1.0.1",
|
"ProductVersion": "4.1.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