Release 4.2.7+1: adaptive HiDPI window sizing (work-area DIPs + min size) and responsive CSS so the UI opens usable without stretch. Windows 4.2.7.1.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Navis
2026-08-02 12:00:10 +03:00
co-authored by Cursor
parent a1756715ef
commit fa3e11216e
15 changed files with 354 additions and 60 deletions
+2 -2
View File
@@ -13,8 +13,8 @@ android {
minSdk = 26
targetSdk = 35
// versionCode = major*1_000_000 + minor*10_000 + patch*100 + build
versionCode = 4_026_001
versionName = "4.2.6+1"
versionCode = 4_027_001
versionName = "4.2.7+1"
vectorDrawables.useSupportLibrary = true
ndk {
abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64")
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="3.9.0.3"
version="4.2.7.1"
processorArchitecture="*"
name="EvilFox.Navis"
type="win32"/>
+10 -4
View File
@@ -76,6 +76,10 @@ func main() {
}
update.CleanupStaleDownloads()
// Before any HWND: declare DPI awareness so CreateWindow/SetSize sizes
// are physical pixels and we can convert DIPs correctly.
enableProcessDPIAware()
log.SetFlags(log.LstdFlags | log.Lshortfile)
cfgPath, err := config.LocateConfig()
@@ -114,15 +118,17 @@ func main() {
// Do NOT auto-download cores on startup — silent network+write looks like malware to AV.
// Sidebar layout (Hiddify-style) needs a wider window than the old column UI.
// Size from monitor work area in DIPs, converted to physical pixels for
// DPI-aware CreateWindow (fixed 920x680 physical collapses CSS <700px on HiDPI).
initW, initH := initialWindowPhysicalSize()
w := webview2.NewWithOptions(webview2.WebViewOptions{
Debug: false,
AutoFocus: true,
DataPath: dataPath,
WindowOptions: webview2.WindowOptions{
Title: "EvilFox",
Width: 920,
Height: 680,
Width: uint(initW),
Height: uint(initH),
Center: true,
IconId: 1,
},
@@ -137,7 +143,7 @@ func main() {
}()
applyWindowIcon(w.Window())
w.SetSize(920, 680, webview2.HintNone)
applyAdaptiveWindowSize(w)
mustBind(w, "getState", a.getState)
mustBind(w, "connect", a.connect)
+215
View File
@@ -0,0 +1,215 @@
//go:build windows
package main
import (
"unsafe"
"github.com/jchv/go-webview2"
"golang.org/x/sys/windows"
)
// DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = (DPI_AWARENESS_CONTEXT)-4
const dpiAwarenessContextPerMonitorAwareV2 = ^uintptr(3)
const (
spiGetWorkArea = 0x0030
monitorDefaultToNearest = 2
)
type monitorInfo struct {
CbSize uint32
RcMonitor windows.Rect
RcWork windows.Rect
DwFlags uint32
}
// enableProcessDPIAware opts into per-monitor DPI before any HWND is created.
// The embedded app.manifest already declares PerMonitorV2; this is a belt-and-
// suspenders call for builds that lose the manifest resource.
func enableProcessDPIAware() {
user32 := windows.NewLazySystemDLL("user32.dll")
if p := user32.NewProc("SetProcessDpiAwarenessContext"); p.Find() == nil {
_, _, _ = p.Call(dpiAwarenessContextPerMonitorAwareV2)
return
}
if p := user32.NewProc("SetProcessDPIAware"); p.Find() == nil {
_, _, _ = p.Call()
}
}
func systemDPI() int {
user32 := windows.NewLazySystemDLL("user32.dll")
if p := user32.NewProc("GetDpiForSystem"); p.Find() == nil {
if r, _, _ := p.Call(); r >= 72 && r <= 480 {
return int(r)
}
}
return 96
}
func windowDPI(hwnd uintptr) int {
if hwnd != 0 {
user32 := windows.NewLazySystemDLL("user32.dll")
if p := user32.NewProc("GetDpiForWindow"); p.Find() == nil {
if r, _, _ := p.Call(hwnd); r >= 72 && r <= 480 {
return int(r)
}
}
}
return systemDPI()
}
func dipsToPhysical(dips, dpi int) int {
if dpi <= 0 {
dpi = 96
}
return (dips*dpi + 48) / 96
}
func physicalToDIPs(px, dpi int) int {
if dpi <= 0 {
dpi = 96
}
return (px*96 + dpi/2) / dpi
}
func primaryWorkAreaPhysical() (w, h int, ok bool) {
var r windows.Rect
user32 := windows.NewLazySystemDLL("user32.dll")
p := user32.NewProc("SystemParametersInfoW")
ret, _, _ := p.Call(spiGetWorkArea, 0, uintptr(unsafe.Pointer(&r)), 0)
if ret == 0 {
return 0, 0, false
}
return int(r.Right - r.Left), int(r.Bottom - r.Top), true
}
func monitorWorkAreaPhysical(hwnd uintptr) (w, h int, ok bool) {
if hwnd == 0 {
return primaryWorkAreaPhysical()
}
user32 := windows.NewLazySystemDLL("user32.dll")
fromWin := user32.NewProc("MonitorFromWindow")
getInfo := user32.NewProc("GetMonitorInfoW")
if fromWin.Find() != nil || getInfo.Find() != nil {
return primaryWorkAreaPhysical()
}
hmon, _, _ := fromWin.Call(hwnd, monitorDefaultToNearest)
if hmon == 0 {
return primaryWorkAreaPhysical()
}
var mi monitorInfo
mi.CbSize = uint32(unsafe.Sizeof(mi))
ret, _, _ := getInfo.Call(hmon, uintptr(unsafe.Pointer(&mi)))
if ret == 0 {
return primaryWorkAreaPhysical()
}
return int(mi.RcWork.Right - mi.RcWork.Left), int(mi.RcWork.Bottom - mi.RcWork.Top), true
}
// computeClientSizeDIPs picks a comfortable client size in DIPs from the
// monitor work area (also expressed as physical px + dpi).
// Target: ~min(1100, workW*0.72) x min(780, workH*0.78), floored near
// 960x680 when the screen allows, always capped with margins.
func computeClientSizeDIPs(workWPhys, workHPhys, dpi int) (w, h int) {
if dpi <= 0 {
dpi = 96
}
workW := physicalToDIPs(workWPhys, dpi)
workH := physicalToDIPs(workHPhys, dpi)
if workW < 320 {
workW = 320
}
if workH < 240 {
workH = 240
}
const marginX, marginY = 64, 64
maxW := workW - marginX
maxH := workH - marginY
if maxW < 480 {
maxW = workW
}
if maxH < 360 {
maxH = workH
}
if maxW < 480 {
maxW = 480
}
if maxH < 360 {
maxH = 360
}
prefW := int(float64(workW) * 0.72)
if prefW > 1100 {
prefW = 1100
}
prefH := int(float64(workH) * 0.78)
if prefH > 780 {
prefH = 780
}
if prefW < 960 && maxW >= 960 {
prefW = 960
}
if prefH < 680 && maxH >= 680 {
prefH = 680
}
if prefW > maxW {
prefW = maxW
}
if prefH > maxH {
prefH = maxH
}
if prefW < 640 && maxW >= 640 {
prefW = 640
}
if prefH < 480 && maxH >= 480 {
prefH = 480
}
return prefW, prefH
}
// initialWindowPhysicalSize estimates CreateWindow size before an HWND exists
// (primary work area + system DPI). go-webview2 treats Width/Height as physical
// pixels when the process is DPI-aware.
func initialWindowPhysicalSize() (w, h int) {
dpi := systemDPI()
ww, wh, ok := primaryWorkAreaPhysical()
if !ok || ww <= 0 || wh <= 0 {
return dipsToPhysical(960, dpi), dipsToPhysical(680, dpi)
}
dipW, dipH := computeClientSizeDIPs(ww, wh, dpi)
return dipsToPhysical(dipW, dpi), dipsToPhysical(dipH, dpi)
}
// applyAdaptiveWindowSize sets HintMin (~720x520 DIPs) then the preferred
// client size from the window's monitor work area, both in physical pixels.
func applyAdaptiveWindowSize(w webview2.WebView) {
if w == nil {
return
}
hwnd := uintptr(w.Window())
dpi := windowDPI(hwnd)
ww, wh, ok := monitorWorkAreaPhysical(hwnd)
if !ok || ww <= 0 || wh <= 0 {
ww, wh, _ = primaryWorkAreaPhysical()
}
dipW, dipH := computeClientSizeDIPs(ww, wh, dpi)
physW := dipsToPhysical(dipW, dpi)
physH := dipsToPhysical(dipH, dpi)
minW := dipsToPhysical(720, dpi)
minH := dipsToPhysical(520, dpi)
if minW > physW {
minW = physW
}
if minH > physH {
minH = physH
}
w.SetSize(minW, minH, webview2.HintMin)
w.SetSize(physW, physH, webview2.HintNone)
}
+4 -4
View File
@@ -1,11 +1,11 @@
{
"version": "4.2.6",
"notes": "4.2.6: fix sidebar RU/EN and theme toggles — applyTheme param shadowed i18n t(), so init threw and click listeners never bound",
"version": "4.2.7",
"notes": "4.2.7: adaptive window size on HiDPI (DIP work-area sizing + min size); responsive CSS breakpoints so sidebar/power button fit without stretch",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "843fd62f7fe0ae09261322fb90d7f281a48472b78e6a9c543b6a9637fe1cc83e",
"sha256": "dd867e27845fafa9417e1658919fc6613947db3c4677c1f35a270cdc72b3c762",
"mandatory": false,
"platforms": {
"android": {
@@ -40,7 +40,7 @@
},
"windows-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "843fd62f7fe0ae09261322fb90d7f281a48472b78e6a9c543b6a9637fe1cc83e",
"sha256": "dd867e27845fafa9417e1658919fc6613947db3c4677c1f35a270cdc72b3c762",
"os": "windows",
"arch": "amd64"
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+4 -4
View File
@@ -1,11 +1,11 @@
{
"version": "4.2.6",
"notes": "4.2.6: fix sidebar RU/EN and theme toggles — applyTheme param shadowed i18n t(), so init threw and click listeners never bound",
"version": "4.2.7",
"notes": "4.2.7: adaptive window size on HiDPI (DIP work-area sizing + min size); responsive CSS breakpoints so sidebar/power button fit without stretch",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "843fd62f7fe0ae09261322fb90d7f281a48472b78e6a9c543b6a9637fe1cc83e",
"sha256": "dd867e27845fafa9417e1658919fc6613947db3c4677c1f35a270cdc72b3c762",
"mandatory": false,
"platforms": {
"android": {
@@ -40,7 +40,7 @@
},
"windows-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "843fd62f7fe0ae09261322fb90d7f281a48472b78e6a9c543b6a9637fe1cc83e",
"sha256": "dd867e27845fafa9417e1658919fc6613947db3c4677c1f35a270cdc72b3c762",
"os": "windows",
"arch": "amd64"
}
+99 -26
View File
@@ -149,7 +149,9 @@
[hidden] { display: none !important; }
html, body {
margin: 0;
width: 100%;
height: 100%;
height: 100dvh;
font-family: Manrope, sans-serif;
color: var(--ink);
background: var(--page-bg);
@@ -158,8 +160,12 @@
.app {
display: grid;
grid-template-columns: 216px 1fr;
grid-template-columns: 216px minmax(0, 1fr);
width: 100%;
height: 100%;
height: 100vh;
height: 100dvh;
min-height: 0;
}
/* ==== Sidebar ==== */
@@ -172,6 +178,8 @@
backdrop-filter: blur(18px) saturate(1.15);
border-right: 1px solid var(--line);
min-width: 0;
min-height: 0;
overflow: hidden;
}
.side .brand-row {
display: flex;
@@ -329,15 +337,26 @@
display: flex;
flex-direction: column;
min-width: 0;
min-height: 0;
overflow: hidden;
height: 100%;
}
.pages {
flex: 1;
flex: 1 1 auto;
min-height: 0;
overflow-x: hidden;
overflow-y: auto;
padding: 18px 20px 8px;
padding: 18px clamp(12px, 2.2vw, 20px) 8px;
-webkit-overflow-scrolling: touch;
}
/* Inactive pages must not paint (WebView2 overflow ghosts from hidden siblings). */
.page { display: none !important; max-width: 640px; margin: 0 auto; }
.page {
display: none !important;
width: 100%;
max-width: min(720px, 100%);
margin: 0 auto;
box-sizing: border-box;
}
.page.active { display: block !important; animation: rise .3s var(--ease) both; }
@keyframes rise {
from { opacity: 0; transform: translateY(10px); }
@@ -381,12 +400,13 @@
border-radius: var(--radius);
border: 1px solid var(--line);
background: var(--hero-bg);
padding: 26px 16px 20px;
padding: clamp(14px, 2.6vw, 26px) clamp(10px, 2vw, 16px) clamp(12px, 2vw, 20px);
margin-bottom: 14px;
text-align: center;
transition: border-color .25s, box-shadow .25s, background .25s;
min-height: 220px;
min-height: clamp(160px, 28vh, 220px);
box-sizing: border-box;
width: 100%;
}
.hero.on {
border-color: rgba(13,138,102,.35);
@@ -394,10 +414,11 @@
background: var(--hero-bg-on);
}
.power-btn {
width: 148px;
height: 148px;
min-width: 148px;
min-height: 148px;
--power-size: clamp(104px, 22vw, 148px);
width: var(--power-size);
height: var(--power-size);
min-width: var(--power-size);
min-height: var(--power-size);
border-radius: 50%;
border: 0;
cursor: pointer;
@@ -407,7 +428,7 @@
flex-direction: column;
align-items: center;
justify-content: center;
gap: 7px;
gap: clamp(4px, 1vw, 7px);
margin: 4px auto 14px;
transition: background .25s, color .25s, transform .12s, box-shadow .25s;
box-shadow: 0 14px 34px rgba(8, 145, 178, .3);
@@ -417,10 +438,13 @@
.power-btn:hover { transform: translateY(-2px); }
.power-btn:active { transform: none; }
.power-btn:disabled { opacity: .6; cursor: not-allowed; transform: none; }
.power-btn svg { width: 40px; height: 40px; }
.power-btn svg {
width: clamp(28px, 6vw, 40px);
height: clamp(28px, 6vw, 40px);
}
.power-btn .power-label {
font-family: Outfit, sans-serif;
font-size: .64rem;
font-size: clamp(.52rem, 1.5vw, .64rem);
font-weight: 800;
letter-spacing: .09em;
line-height: 1;
@@ -526,11 +550,12 @@
/* Current server card (home, bottom) */
.cur-server {
display: grid;
grid-template-columns: auto 1fr auto;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: center;
gap: 12px;
gap: clamp(8px, 1.5vw, 12px);
width: 100%;
padding: 12px 14px;
max-width: 100%;
padding: clamp(10px, 1.8vw, 12px) clamp(10px, 1.8vw, 14px);
border-radius: var(--radius);
border: 1px solid var(--line);
background: var(--surface-2);
@@ -540,6 +565,7 @@
text-align: left;
margin-bottom: 12px;
transition: border-color .15s, background .15s;
box-sizing: border-box;
}
.cur-server:hover { border-color: rgba(8,145,178,.35); }
.cur-server .flag {
@@ -1217,10 +1243,13 @@
border: 1px solid var(--line);
border-radius: var(--radius);
background: var(--row-bg);
padding: 12px 14px;
padding: clamp(10px, 1.8vw, 12px) clamp(10px, 1.8vw, 14px);
margin-bottom: 12px;
display: grid;
gap: 8px;
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
.traffic-row {
display: flex;
@@ -1256,7 +1285,10 @@
border: 1px solid var(--line);
border-radius: var(--radius);
background: var(--surface-2);
padding: 14px;
padding: clamp(10px, 1.8vw, 14px);
width: 100%;
max-width: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 8px;
@@ -1371,12 +1403,53 @@
.modal h2 { font-family: Outfit, sans-serif; margin: 0 0 12px; font-size: 1.2rem; letter-spacing: -.02em; }
.modal .actions { margin-top: 12px; grid-template-columns: 1fr 1fr; }
/* Medium: keep labels, slightly narrower sidebar */
@media (max-width: 900px) and (min-width: 701px) {
.app { grid-template-columns: 176px minmax(0, 1fr); }
.side { padding: 14px 8px 10px; }
.nav-btn { gap: 10px; padding: 10px 12px; font-size: .84rem; }
.brand { font-size: 1.2rem; }
}
/* Narrow: icon sidebar — main pane stays usable (fluid power/cards) */
@media (max-width: 700px) {
.app { grid-template-columns: 64px 1fr; }
.side .brand-row { justify-content: center; padding-left: 0; padding-right: 0; }
.app { grid-template-columns: 64px minmax(0, 1fr); }
.side {
padding: 12px 6px 10px;
align-items: stretch;
}
.side .brand-row { justify-content: center; padding-left: 0; padding-right: 0; padding-bottom: 10px; }
.side .brand, .side .badge-ver, .nav-btn .nav-label, .side-foot .ver { display: none; }
.nav-btn { justify-content: center; padding: 12px 0; }
.side-foot { justify-content: center; }
.side-foot {
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
padding: 8px 0 0;
}
.side-foot-controls {
flex-direction: column;
align-items: center;
gap: 8px;
width: 100%;
}
.lang-seg {
flex-direction: column;
height: auto;
width: 40px;
}
.lang-seg button {
min-width: 0;
width: 100%;
padding: 7px 0;
}
.theme-toggle { width: 40px; height: 40px; }
.pages { padding: 12px 10px 8px; }
.page { max-width: 100%; }
.hero { min-height: 0; }
.power-btn { --power-size: clamp(96px, 28vw, 132px); }
.update-banner { margin-left: 10px; margin-right: 10px; }
details.panel .grid2 { grid-template-columns: 1fr; }
.tools { grid-template-columns: 1fr; }
}
@@ -1392,23 +1465,23 @@
<span class="badge-ver" id="badgeVer" hidden></span>
</div>
<button class="nav-btn active" type="button" data-page="guard">
<button class="nav-btn active" type="button" data-page="guard" title="Защита" data-i18n-title="nav_guard">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3l7.5 3v5.2c0 4.6-3.1 8-7.5 9.8-4.4-1.8-7.5-5.2-7.5-9.8V6z"/><path d="M9 12l2.2 2.2L15.4 10"/></svg>
<span class="nav-label" data-i18n="nav_guard">Защита</span>
</button>
<button class="nav-btn" type="button" data-page="profiles">
<button class="nav-btn" type="button" data-page="profiles" title="Мои серверы" data-i18n-title="nav_profiles">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="3.5" y="4" width="17" height="6.5" rx="2"/><rect x="3.5" y="13.5" width="17" height="6.5" rx="2"/><path d="M7 7.2h.01M7 16.7h.01"/></svg>
<span class="nav-label" data-i18n="nav_profiles">Мои серверы</span>
</button>
<button class="nav-btn" type="button" data-page="settings">
<button class="nav-btn" type="button" data-page="settings" title="Настройки" data-i18n-title="nav_settings">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3.2"/><path d="M19.4 13.5a7.6 7.6 0 0 0 0-3l2-1.5-2-3.4-2.3 1a7.7 7.7 0 0 0-2.6-1.5L14.2 2h-4l-.3 2.6a7.7 7.7 0 0 0-2.6 1.5l-2.3-1-2 3.4 2 1.5a7.6 7.6 0 0 0 0 3l-2 1.5 2 3.4 2.3-1a7.7 7.7 0 0 0 2.6 1.5l.3 2.6h4l.3-2.6a7.7 7.7 0 0 0 2.6-1.5l2.3 1 2-3.4z"/></svg>
<span class="nav-label" data-i18n="nav_settings">Настройки</span>
</button>
<button class="nav-btn" type="button" data-page="logs">
<button class="nav-btn" type="button" data-page="logs" title="Статистика" data-i18n-title="nav_stats">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M5 20V10M12 20V4M19 20v-7"/></svg>
<span class="nav-label" data-i18n="nav_stats">Статистика</span>
</button>
<button class="nav-btn" type="button" data-page="about">
<button class="nav-btn" type="button" data-page="about" title="Помощь" data-i18n-title="nav_help">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M9.6 9.2a2.5 2.5 0 1 1 3.5 2.3c-.8.4-1.1 1-1.1 1.8"/><path d="M12 16.6h.01"/></svg>
<span class="nav-label" data-i18n="nav_help">Помощь</span>
</button>
+9 -9
View File
@@ -17,8 +17,8 @@ import (
)
// CurrentVersion is the product/semver used for update eligibility (feed "version").
// Keep major.minor.patch only no build suffix here.
const CurrentVersion = "4.2.6"
// Keep major.minor.patch only — no build suffix here.
const CurrentVersion = "4.2.7"
// BuildNumber is the monotonic build within CurrentVersion (Windows FileVersion 4th part,
// macOS CFBundleVersion suffix, Android versionCode low digits). Bump on every release build.
@@ -75,7 +75,7 @@ type Status struct {
Checking bool `json:"checking,omitempty"`
Error string `json:"error,omitempty"`
ManifestURL string `json:"manifest_url"`
StoreManaged bool `json:"store_managed,omitempty"` // MSIX / Microsoft Store no in-app updates
StoreManaged bool `json:"store_managed,omitempty"` // MSIX / Microsoft Store — no in-app updates
}
// PlatformKey is "GOOS-GOARCH", e.g. windows-amd64, darwin-arm64.
@@ -86,7 +86,7 @@ func PlatformKey() string {
// AssetFor returns download URL + checksum for the given platform key.
//
// Rules (critical):
// - Only the asset for this OS/arch is considered never another OS.
// - Only the asset for this OS/arch is considered — never another OS.
// - If the feed has a newer version but no matching platform asset, ok=false
// (caller must treat Available as false).
// - Darwin may fall back to "darwin-universal" when the exact arch key is absent.
@@ -196,7 +196,7 @@ func Check(ctx context.Context, manifestURL string) (Status, error) {
if IsStorePackaged() {
st.StoreManaged = true
st.Available = false
st.Notes = "Обновления через Microsoft Store"
st.Notes = "Обновления через Microsoft Store"
return st, nil
}
@@ -212,7 +212,7 @@ func Check(ctx context.Context, manifestURL string) (Status, error) {
url, sha, ok := m.AssetFor(key)
if !ok {
// Higher feed version for another OS must NOT count as available here.
st.Error = fmt.Sprintf("нет сборки для %s", key)
st.Error = fmt.Sprintf("нет сборки для %s", key)
st.Available = false
return st, nil
}
@@ -254,7 +254,7 @@ func productParts(v string) [3]int {
func splitVer(v string) []int {
v = strings.TrimPrefix(strings.TrimSpace(v), "v")
// Strip build metadata: 2.7.2+1 / 2.7.2-beta compare product only.
// Strip build metadata: 2.7.2+1 / 2.7.2-beta в†’ compare product only.
if i := strings.IndexAny(v, "+-"); i >= 0 {
v = v[:i]
}
@@ -353,10 +353,10 @@ func prepareDownload(ctx context.Context, manifestURL string) (latest, url, sha,
if st.Error != "" {
return "", "", "", "", "", fmt.Errorf("%s", st.Error)
}
return "", "", "", "", "", fmt.Errorf("уже актуальная версия %s (канал %s)", DisplayVersion(), st.Latest)
return "", "", "", "", "", fmt.Errorf("уже актуальная версия %s (канал %s)", DisplayVersion(), st.Latest)
}
if !allowedDownloadURL(st.URL) {
return "", "", "", "", "", fmt.Errorf("URL обновления должен быть на git.de4ima.uk, git.evilfox.cc или files.de4ima.uk")
return "", "", "", "", "", fmt.Errorf("URL обновления должен быть на git.de4ima.uk, git.evilfox.cc или files.de4ima.uk")
}
exe, err := os.Executable()
if err != nil {
+1 -1
View File
@@ -33,7 +33,7 @@
<Identity
Name="EvilFox.EvilFox"
Publisher="CN=EvilFox"
Version="4.2.6.1"
Version="4.2.7.1"
ProcessorArchitecture="x64" />
<Properties>
+4 -4
View File
@@ -1,11 +1,11 @@
{
"version": "4.2.6",
"notes": "4.2.6: fix sidebar RU/EN and theme toggles — applyTheme param shadowed i18n t(), so init threw and click listeners never bound",
"version": "4.2.7",
"notes": "4.2.7: adaptive window size on HiDPI (DIP work-area sizing + min size); responsive CSS breakpoints so sidebar/power button fit without stretch",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "843fd62f7fe0ae09261322fb90d7f281a48472b78e6a9c543b6a9637fe1cc83e",
"sha256": "dd867e27845fafa9417e1658919fc6613947db3c4677c1f35a270cdc72b3c762",
"mandatory": false,
"platforms": {
"android": {
@@ -40,7 +40,7 @@
},
"windows-amd64": {
"url": "https://git.de4ima.uk/Evilfox/navi/raw/branch/Windows/dist/navis-release/windows/EvilFox.exe",
"sha256": "843fd62f7fe0ae09261322fb90d7f281a48472b78e6a9c543b6a9637fe1cc83e",
"sha256": "dd867e27845fafa9417e1658919fc6613947db3c4677c1f35a270cdc72b3c762",
"os": "windows",
"arch": "amd64"
}
+4 -4
View File
@@ -1,7 +1,7 @@
{
"FixedFileInfo": {
"FileVersion": { "Major": 4, "Minor": 2, "Patch": 6, "Build": 1 },
"ProductVersion": { "Major": 4, "Minor": 2, "Patch": 6, "Build": 1 },
"FileVersion": { "Major": 4, "Minor": 2, "Patch": 7, "Build": 1 },
"ProductVersion": { "Major": 4, "Minor": 2, "Patch": 7, "Build": 1 },
"FileFlagsMask": "3f",
"FileFlags": "00",
"FileOS": "40004",
@@ -11,12 +11,12 @@
"StringFileInfo": {
"CompanyName": "EvilFox",
"FileDescription": "EvilFox — VPN client (Naive / Hy2 / AWG / VLESS / VMess / Trojan)",
"FileVersion": "4.2.6.1",
"FileVersion": "4.2.7.1",
"InternalName": "EvilFox",
"LegalCopyright": "Copyright (c) EvilFox",
"OriginalFilename": "EvilFox.exe",
"ProductName": "EvilFox",
"ProductVersion": "4.2.6.1",
"ProductVersion": "4.2.7.1",
"Comments": "Open-source VPN/proxy client. https://evilfox.win/"
},
"VarFileInfo": {